home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / ihpfs124.zip / ihpfs.asm < prev    next >
Assembly Source File  |  1997-06-01  |  121KB  |  4,646 lines

  1. ;
  2. ; iHPFS - An Installable HPFS Driver for DOS
  3. ; Copyright (C) 1993-1997 Marcus Better
  4. ;
  5. ; This program is free software; you can redistribute it and/or modify
  6. ; it under the terms of the GNU General Public License as published by
  7. ; the Free Software Foundation; either version 2 of the License, or
  8. ; (at your option) any later version.
  9. ;
  10. ; This program is distributed in the hope that it will be useful,
  11. ; but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. ; GNU General Public License for more details.
  14. ;
  15. ; You should have received a copy of the GNU General Public License
  16. ; along with this program; if not, write to the Free Software
  17. ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. ;
  19.  
  20. ;==================================================================
  21. ; This program is written for Turbo Assembler 3.2, and uses ideal
  22. ; mode syntax. I use the following commands to build iHPFS:
  23. ;       tasm /la /zn /t /m ihpfs.asm
  24. ;       tlink ihpfs.obj
  25. ;==================================================================
  26.  
  27. IDEAL
  28. P386
  29. JUMPS
  30. LOCALS
  31.  
  32. ;------------------------------------------------- Constants
  33. ; Version
  34. VerMajor    EQU    1
  35. VerMinor    EQU    24
  36.  
  37. ; Errors
  38. errFileNotFound EQU     02h
  39. errPathNotFound EQU     03h
  40. errTooManyFiles EQU     04h
  41. errAccessDenied EQU     05h
  42. errInvalidHandle EQU    06h
  43. errNoMoreFiles  EQU     12h
  44. errWriteProt    EQU     13h
  45. errSectorNotFound EQU   1Bh
  46.  
  47. TimeZone        EQU     -3600
  48. MinCacheSize    EQU     32      ; Minimum cache size in KB
  49. MaxCacheSize    EQU     32768   ; Maximum cache size in KB
  50. LoadFactor      EQU     5       ; Elements/chain in hash.
  51. CacheEntrySize    EQU    14    ; Cache entry size
  52. MaxPathLength    EQU    57    ; Maximum length of pathname in DOS
  53.  
  54. ; Disk access methods
  55. Method_CHS    EQU    0    ; CHS addressing
  56. Method_CHSExt    EQU    1    ; Extended CHS addressing
  57. Method_Ext    EQU    2    ; IBM/MS Extensions
  58. ;------------------------------------------------- Macros
  59. MACRO   Abort   Code
  60.     mov     bx, Code
  61.     call    AbortMsg
  62. ENDM
  63.  
  64. MACRO    NOASSUME
  65.     ASSUME    cs:NOTHING,ds:NOTHING,es:NOTHING,fs:NOTHING,gs:NOTHING,ss:NOTHING
  66. ENDM
  67.  
  68. MACRO    DEFASSUME
  69.     ASSUME    cs:ResCode,ds:ResCode,es:NOTHING,fs:NOTHING,gs:ResData,ss:NOTHING
  70. ENDM
  71.  
  72. ;------------------------------------------------- Structures
  73. STRUC   XMSMoveStruct
  74.   Length        DD      0       ; Number of bytes to transfer
  75.   SourceHandle  DW      0       ; Handle of source block
  76.   SourceOffset  DD      0       ; Offset into source block
  77.   DestHandle    DW      0       ; Handle of dest block
  78.   DestOffset    DD      0       ; Offset into dest block
  79. ENDS    XMSMoveStruct
  80.  
  81. STRUC    DiskAddrPacketStruct    ; IBM/MS Extensions disk address packet
  82.   Length    DB    10h    ; Length of packet
  83.         DB    0
  84.   Count        DW    0    ; Number of blocks to transfer
  85.   Buffer    DD    0    ; Address of transfer buffer
  86.   Sector    DQ    0    ; Starting absolute sector number
  87. ENDS    DiskAddrPacketStruct
  88.  
  89. ;------------------------------------------------- Procedures
  90. PROCDESC DiskRead PASCAL NEAR :DWORD,:WORD,:DWORD
  91. PROCDESC ReadSector PASCAL NEAR :DWORD,:WORD,:DWORD
  92. PROCDESC ReadFileSector PASCAL NEAR :DWORD,:WORD,:DWORD,:DWORD
  93. PROCDESC ChkPathLength PASCAL NEAR :DWORD
  94. PROCDESC ScanPartTbl PASCAL NEAR :WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:DWORD
  95. PROCDESC CheckHPFSPart PASCAL NEAR :WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:WORD,:DWORD
  96. PROCDESC ReadSectorExtCHS PASCAL NEAR :DWORD,:WORD,:WORD,:WORD,:DWORD
  97. PROCDESC CheckCylNumber PASCAL NEAR :DWORD,:WORD,:WORD
  98. PROCDESC IsInstalledPart PASCAL NEAR :WORD
  99. PROCDESC QueryPart PASCAL NEAR :WORD,:WORD
  100. PROCDESC RemoveDrv PASCAL NEAR :WORD,:WORD
  101. PROCDESC QueryDrive PASCAL NEAR :WORD,:WORD
  102. PROCDESC UninstallDriver PASCAL NEAR :WORD
  103. ;======================================================= Resident section
  104. SEGMENT ResCode
  105.     ASSUME  cs:ResCode,ds:NOTHING,es:NOTHING,ss:NOTHING,fs:NOTHING,gs:NOTHING
  106. ;-------------------------------------------------- INT 2D entry
  107. ; Follows the Alternate Multiplex Interrupt Specification (AMIS) [v3.5.1]
  108. ; This is an IBM interrupt sharing protocol entry point.
  109. ; The entry point is located in the beginning of the segment,
  110. ; so that the rest of the segment may be released on uninstall.
  111. Int2DEntry:
  112.     jmp    SHORT JmpToInt2DHandler
  113. OldInt2D DD    0    ; Saved vector for next handler in chain
  114.     DW    424Bh    ; Protocol signature
  115.     DB    00h    ; EOI flag - software interrupt
  116.     jmp    SHORT HardwareReset2D    ; Hardware reset routine
  117.     DB    7 DUP (0) ; Reserved
  118. JmpToInt2DHandler:
  119.     jmp    Int2DHandler
  120. HardwareReset2D:
  121.     retf
  122.  
  123. ;-------------------------------------------------- INT 2F entry        
  124. ; This is an IBM interrupt sharing protocol entry point.
  125. Int2FEntry:
  126.     jmp     SHORT JmpToInt2FHandler
  127. OldInt2F DD     0       ; Saved vector for next handler in chain
  128.     DW    424Bh    ; Protocol signature
  129.     DB    00h    ; EOI flag - software interrupt.
  130.     jmp    SHORT HardwareReset2F ; Hardware reset routine
  131.     DB    7 DUP (0) ; Reserved
  132. JmpToInt2FHandler:
  133.     jmp     Int2FHandler
  134. HardwareReset2F:
  135.     retf
  136. EndUninstalledCode:    ; Last byte to keep when uninstalled
  137.  
  138. ;-------------------------------------------------- Common resident data
  139. DataSegs DW    26 DUP(0)    ; Data segments for the drives
  140. ; AMIS information
  141. AMISSign DB    "M Better"    ; Manufacturer name
  142.     DB    "iHPFS   "    ; Product name
  143.     DB    "HPFS Driver for DOS", 0 ; Description, ASCIIZ.
  144. HookList DB    2Fh
  145.     DW    OFFSET Int2FEntry
  146.     DB    2Dh
  147.     DW    OFFSET Int2DEntry
  148. ApiFunc DB     0                ; INT 2D function # for API.
  149. ; Saved registers and vectors
  150. FuncAddr DW    0        ; Pointer to current redirector function
  151. ResDataSeg DW    0        ; Current resident data segment
  152. DriveNo DB      0               ; Current drive number
  153. SaveSP  DW      0        ; SP on entry to interrupt handler
  154. SaveSS  DW      0        ; SS on entry to interrupt handler
  155. Novell    DB    0        ; Set if Novell DOS
  156. ; Pointers to SDA fields. Layout:
  157. ;                DOS4+    DOS 3, DR-DOS
  158. ;  DTA ptr            0Ch    0Ch
  159. ;  First filename buffer    9Eh    92h
  160. ;  Search data block        19Eh    192h
  161. ;  Dir entry for found file    1B3h    1A7h
  162. ;  Search attributes        24Dh    23Ah
  163. ;  File access/sharing mode    24Eh    23Bh
  164. ;  Ptr to current CDS        282h    26Ch
  165. ;  Extended open mode        2E1h    Not supported
  166. SDA    DD    0               ; Address of DOS Swappable Data Area
  167. PSP    DW      0           ; Program Segment Prefix
  168. pCurrCDS DD    282h        ; Pointer to current CDS
  169. pDTA    DD    0Ch        ; Pointer to current DTA
  170. FN1    DD    9Eh        ; Address of first filename field
  171. AccMode    DD    24Eh        ; Address of file access/sharing mode field
  172. SrchAttr DD    24Dh        ; Address of search attributes
  173. ExtOpenMode DD    2E1h        ; Address of extended open mode
  174. ; Buffers.
  175. Buf1    DB      512 DUP(0)
  176. Buf2    DB      512 DUP(0)
  177. Buf3    DB      512 DUP(0)
  178. Buf4    DB      512 DUP(0)
  179. FNameBuf DB     128 DUP(0)
  180. FNameBuf2 DB    128 DUP(0)
  181. BufUsed    DB    0    ; Flag for FindNext, set if buffers untouched.
  182. ; Some flags
  183. Multitrack DB   1       ; Allow multitrack reads and writes.
  184. ; Characters permitted in filenames, etc
  185. MinPerm DB      0       ; Lowest permissible character
  186. MaxPerm DB      255     ; Highest permissible character
  187. MinExcl DB      0       ; Lowest excluded character
  188. MaxExcl DB      0       ; Highest excluded character
  189. NumTerm DB      0       ; Number of illegal (terminator) characters
  190. TermChars DB    32 DUP (0) ; Array of illegal characters
  191. UpCaseTbl DB    128 DUP (0) ; File Character Upper-Case Table
  192. DaysInMonth DB  31,28,31,30,31,30,31,31,30,31,30,31
  193. ConvertLong DB    0    ; Convert long filenames flag
  194. ConvTable DB    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_!#%"
  195. ; Cache info
  196. CacheOn DB      0       ; Flag: Caching active?
  197. XMSEntry DD     0       ; XMS driver entry point
  198. hHashTable DW   0       ; XMS handle to the hash table
  199. hCacheLists DW  0       ; XMS handle to the hash chains
  200. hCacheSectors DW 0      ; XMS handle to the cache sectors
  201. CacheEntries DW 0       ; Entries in the cache
  202. HashSize DW     0       ; Slots in the hash table
  203. FreeEntry DW    0       ; Next free entry pointer
  204. XMoveStruc XMSMoveStruct <>
  205. XMSError DB     0       ; Set if XMS call failed
  206. EntryBuf DB     20 DUP (0)
  207. ; IBM/MS Extensions data
  208. DiskAddrPkt DiskAddrPacketStruct <>
  209. ; Jump Table, 0 means unsupported
  210. JmpTbl  DW      0, RmDir, 0, MkDir
  211.     DW      0, ChDir, Close, Commit
  212.     DW      Read, Write, LockRegion, UnlockRegion
  213.     DW      DiskFree, 0, SetAttrib, GetAttrib
  214.     DW      0, Rename, 0, Delete
  215.     DW      0, 0, Open, Create
  216.     DW      0, 0, 0, FindFirst
  217.     DW      FindNext, 0, 0, 0
  218.     DW      0, Seek, 0, 0
  219.     DW      0, 0, 0, 0
  220.     DW      0, 0, 0, 0
  221.     DW      0, 0, ExtOpen
  222.  
  223. ;------------------------------------------------------------------------
  224. ; Int 2D (Alternate Multiplex) handler.
  225. PROC    Int2DHandler FAR
  226.     NOASSUME
  227.     ASSUME    cs:ResCode
  228.     cmp    ah, [ApiFunc]
  229.     je    apiApiCall
  230.     jmp    [OldInt2D]
  231. apiApiCall:
  232.     or      al, al          ; Installation check
  233.     je      apiInstallChk
  234.     cmp     al, 2           ; Uninstall
  235.     je      apiUnInstall
  236.     cmp    al, 4        ; Determine chained interrupts
  237.     je    apiGetHookList
  238.     cmp    al, 10h        ; Query drive installed
  239.     je    apiQueryDrive
  240.         xor    al, al        ; Not implemented
  241.     iret
  242.  
  243. ; iHPFS installation check.
  244. apiInstallChk:        
  245.     mov     al, 0FFh        ; Multiplex number in use
  246.     mov    ch, VerMajor
  247.     mov    cl, VerMinor
  248.     mov    dx, cs
  249.     mov    di, OFFSET AMISSign ; DX:DI -> AMIS signature
  250.     iret
  251.  
  252. apiGetHookList:
  253.     mov    dx, cs
  254.     mov    bx, OFFSET HookList
  255.     mov    al, 04h        ; Hook list returned.
  256.     iret
  257.  
  258. apiUnInstall:
  259.     mov    bx, ResCode
  260.     mov    al, 03h
  261.     iret
  262.  
  263. ; Query drive installed. Drive number in BX (will be destroyed). Returns
  264. ; AH=1 if installed, AH=0 otherwise.
  265. apiQueryDrive:
  266.     shl    bx, 1
  267.     cmp    [DataSegs+bx], 0
  268.     setnz    ah
  269.     iret
  270.  
  271. ENDP    Int2DHandler
  272.  
  273. ;------------------------------------------------------------------------
  274. ; Int 2F (Multiplex) Interrupt handler.
  275. ; Processes calls for function 11h.
  276. PROC    Int2FHandler FAR
  277.     NOASSUME
  278.     ASSUME  cs:ResCode
  279.     sti
  280.     cmp     ah, 11h
  281.     je      Function11
  282.     jmp     [OldInt2F]
  283.  
  284. Function11:
  285. ; Decide which method to use for determining if the call is for us.
  286.     or      al, al
  287.     jz      NetInstallChk   ; Redirector installation check
  288.     cmp     al, 21h         ; Seek
  289.     je      CheckSFT
  290. ;        cmp     al, 1Ch         ; Find next
  291. ;        je      CheckFindNext
  292.     cmp     al, 2Eh
  293.     je      CheckCDS
  294.     cmp     al, 1Dh
  295.     jbe     Function11_1
  296.     jmp     [OldInt2F]
  297. Function11_1:
  298.     cmp     al, 06h
  299.     jb      CheckCDS
  300.     cmp     al, 0Bh
  301.     jna     CheckSFT
  302.  
  303. ; CDS method: Check path field of CDS.
  304. CheckCDS:
  305.     push    ds bx
  306.     lds    bx, [pCurrCDS]
  307.     lds    bx, [bx]        ; CDS for current file
  308.     movzx    bx, [BYTE bx]
  309.     cmp    bl, '\'
  310.     je    CheckCDS1        ; Not for us - ZF set
  311.     sub    bl, 'A'
  312.     mov    [DriveNo], bl
  313.     shl    bx, 1
  314.     mov    bx, [DataSegs+bx]
  315.     mov    [ResDataSeg], bx
  316.     or    bx, bx            ; ZF set if unsupported drive
  317. CheckCDS1:
  318.     pop     bx ds
  319.     jnz     CallForUs
  320.     jmp     [DWORD OldInt2F] ; Call was not for us
  321.  
  322. ; SFT method: Check drive number in SFT entry.
  323. ; ES:DI -> SFT entry for file
  324. CheckSFT:
  325.     push    bx
  326.     movzx   bx, [BYTE es:di+5]
  327.     and     bl, 3Fh                 ; Bits 5-0 contain drive number
  328.     mov    [DriveNo], bl
  329.     shl    bx, 1
  330.     mov    bx, [DataSegs+bx]
  331.     mov    [ResDataSeg], bx
  332.     or    bx, bx
  333.     pop     bx
  334.     jnz     CallForUs
  335.     jmp     [DWORD OldInt2F]
  336.  
  337. ; Special check for Find Next function - drive number in SDB.
  338. ; Doesn't seem to work with MS-DOS 7 / Windows95.
  339. ;CheckFindNext:
  340. ;        push    ds bx
  341. ;        lds     bx, [pDTA]
  342. ;        lds     bx, [bx]                ; DS:BX -> DTA
  343. ;        mov     bl, [BYTE es:di]        ; SDB drive number
  344. ;        and     bx, 3Fh                 ; Turn off network bit
  345. ;        mov     [DriveNo], bl
  346. ;        shl     bx, 1
  347. ;        mov     bx, [DataSegs+bx]
  348. ;        mov     [ResDataSeg], bx
  349. ;        or      bx, bx
  350. ;        pop     bx ds
  351. ;        jnz     CallForUs
  352. ;        jmp     [DWORD OldInt2F]
  353.  
  354. ; Call is for our drive
  355. CallForUs:
  356. ; Switch stack
  357.     mov     [cs:SaveSP], sp
  358.     mov     [cs:SaveSS], ss
  359.     push    cs
  360.     pop     ss
  361.     mov     sp, OFFSET ResStack
  362.     ASSUME    ss:SEG ResStack
  363. ; Transfer to the subfunction handler
  364.     push    bx
  365.     mov     bl, al
  366.     xor     bh, bh
  367.     shl     bl, 1
  368.     mov    bx, [JmpTbl+bx]
  369.     mov    [FuncAddr], bx
  370.     pop    bx
  371.     cmp    [FuncAddr], 0
  372.     jz      Unsupported
  373.     push    ds gs
  374.     push    cs
  375.     pop    ds
  376.     mov    gs, [ResDataSeg]
  377.     DEFASSUME
  378.     call    [FuncAddr]
  379.     pop    gs ds
  380.     NOASSUME
  381.     ASSUME    cs:ResCode
  382.     lss     sp, [DWORD SaveSP]
  383.     ret     2
  384. Unsupported:
  385.     lss     sp, [DWORD SaveSP]
  386.     jmp     [DWORD OldInt2F] ; Function not supported, ignore it.
  387.  
  388. ; Redirector installed check
  389. NetInstallChk:
  390.     mov     ax, 00FFh       ; Redirector installed.
  391.     iret                    ; Return immediately
  392. ENDP    Int2FHandler
  393.  
  394. ;---------------------------------------------------------------------
  395. ; Remove Directory
  396. PROC    RmDir
  397.     stc
  398.     mov     ax, errPathNotFound
  399.     ret
  400. ENDP    RmDir
  401.  
  402. ;---------------------------------------------------------------------
  403. ; Make Directory
  404. PROC    MkDir
  405.     stc
  406.     mov     ax, errWriteProt
  407.     ret
  408. ENDP    MkDir
  409.  
  410. ;---------------------------------------------------------------------
  411. ; Change Directory
  412. PROC    ChDir   STDCALL
  413.     LOCAL   @@CDSPointer:DWORD, @@Result
  414.     DEFASSUME
  415.     pushad
  416.     push    es
  417.     mov    [BufUsed], 0
  418.     les    di, [pCurrCDS]
  419.     mov    eax, [es:di]
  420.     mov     [@@CDSPointer], eax
  421.     les     di, [FN1]
  422.     cmp     [BYTE es:di+3], 0    ; See if root directory
  423.     je      @@Root
  424.     cmp    [BYTE es:di+2], 0    ; See if root in DR-DOS
  425.     je    @@Root
  426.     call    FindFile
  427.     jc      @@PathNotFound
  428. ; See if the entry is a subdirectory
  429.     test    [Buf1+bx+03h], 10h    ; Mask out subdirectory attribute
  430.     jz      @@PathNotFound
  431.     call    NEAR ChkPathLength PASCAL, [FN1]
  432.     jc    @@PathNotFound
  433. ; Extract the FNode pointer
  434.     mov     ecx, [DWORD Buf1+bx+04h]
  435.     mov     [CDFNode], ecx
  436. ; Set the CDS directory name to current directory.
  437.     lds     si, [FN1]
  438.     ASSUME    ds:NOTHING
  439.     les     di, [@@CDSPointer]
  440.     mov     cx, 67            ; Length of CDS path string
  441.     cld
  442. @@MovePath:
  443.     lodsb
  444.     stosb
  445.     or      al, al
  446.     loopnz  @@MovePath
  447.     clc
  448.     jmp     @@Done
  449.  
  450.     DEFASSUME
  451. @@Root: mov     eax, [RootFNode]
  452.     mov     [CDFNode], eax
  453.     lds     bx, [@@CDSPointer]
  454.     ASSUME    ds:NOTHING
  455.     mov     [BYTE bx+3], 0      ; Puts 0 after X:\ in CDS Filename
  456.     clc
  457.     jmp     @@Done
  458. @@PathNotFound:
  459.     stc
  460.     mov     [@@Result], errPathNotFound
  461. @@Done:
  462.     pop     es
  463.     popad
  464.     jnc     @@Exit
  465.     mov     ax, [@@Result]
  466. @@Exit:
  467.     ret
  468. ENDP    ChDir
  469.  
  470. ;---------------------------------------------------------------------
  471. ; Close File
  472. PROC    Close
  473.     DEFASSUME
  474.     push    ax bx
  475.     mov     ax, 1208h
  476.     int     2Fh             ; Decrease handle count in SFT
  477.     cmp     ax, 01h         ; Last handle closed?
  478.     jne     @@1
  479. ; Clear the SFT
  480.     mov     [WORD es:di], 0     ; Number of references
  481. @@1:
  482.     pop     bx ax
  483.     ret
  484. ENDP    Close
  485.  
  486. ;---------------------------------------------------------------------
  487. ; Commit File
  488. PROC    Commit
  489.     stc
  490.     mov     ax, errWriteProt
  491.     ret
  492. ENDP    Commit
  493.  
  494. ;---------------------------------------------------------------------
  495. ; Read from File
  496. PROC    Read    STDCALL
  497.     DEFASSUME
  498.     LOCAL   @@OrigBytes, @@Bytes, @@RelSect:DWORD, @@SecOfs, @@DTABuf:DWORD
  499.         LOCAL   @@Result, @@SFTOfs, @@FNode:DWORD
  500.     pushad
  501.     push    es fs
  502.     push    es
  503.     pop     fs
  504.     ASSUME    fs:NOTHING
  505.     mov     [@@SFTOfs], di
  506.     mov    [BufUsed], 0
  507.     mov     [@@Bytes], cx
  508.     mov    [@@OrigBytes], 0
  509.     les     bx, [SDA]
  510.     les     bx, [DWORD es:bx+0Ch]       ; DTA Pointer
  511.     mov     [WORD LOW @@DTABuf], bx
  512.     mov     [WORD HIGH @@DTABuf], es
  513.         mov     eax, [fs:di+19h]
  514.         mov     [@@FNode], eax
  515.     push    cs
  516.         pop    es
  517.     ASSUME    es:ResCode
  518. ; Adjust bytes to read if necessary.
  519.     movzx   eax, [@@Bytes]           ; Bytes to read
  520.     mov    edx, [fs:di+15h]    ; File pos
  521.     mov    ecx, [fs:di+11h]    ; File size
  522.     cmp    edx, ecx
  523.     jae    @@Succeed        ; Beyond EOF
  524.     add     eax, edx
  525.     cmp     eax, ecx        ; Compare w file size
  526.     jna     @@1
  527.     sub     eax, ecx
  528.     sub     [@@Bytes], ax           ; Actual bytes to read
  529. @@1:    mov     ax, [@@Bytes]
  530.     mov     [@@OrigBytes], ax
  531.     mov     eax, edx        ; File pos
  532.     and     dx, 511
  533.     mov     [@@SecOfs], dx
  534.     shr     eax, 9
  535.     mov     [@@RelSect], eax
  536.     cmp    [@@SecOfs], 0
  537.     je    @@WholeSectors
  538. ; Read sector into Buf2 and copy bytes to DTA
  539.         call    NEAR ReadFileSector, [@@RelSect], 1, [@@FNode], ds (OFFSET Buf2)
  540.     jc    @@ReadError
  541.     inc     [@@RelSect]
  542.     mov     cx, 512
  543.     sub     cx, [@@SecOfs]            ; Bytes to read from this sector
  544.     cmp     [@@Bytes], cx
  545.     ja      @@2
  546.     mov     cx, [@@Bytes]
  547. @@2:
  548.     mov     si, OFFSET Buf2
  549.     add     si, [@@SecOfs]
  550.     les     di, [@@DTABuf]
  551.     ASSUME    es:NOTHING
  552.     push    cx
  553.     rep movsb                       ; Transfer bytes to DTA Buffer
  554.     pop     cx
  555.     add     [WORD @@DTABuf], cx     ; Increase user buffer offset
  556.     sub     [@@Bytes], cx           ; Decrease bytes left to read
  557. ; Read sectors into DTA
  558. @@WholeSectors:    
  559.     cmp     [@@Bytes], 512
  560.     jb      @@LastSector            ; Done if no more bytes
  561.     movzx    eax, [@@Bytes]
  562.     shr    ax, 9            ; Sectors to read
  563.         call    NEAR ReadFileSector, [@@RelSect], ax, [@@FNode], [@@DTABuf]
  564.     jc      @@ReadError
  565.     add    [@@RelSect], eax
  566.     shl    ax, 9            ; Bytes read
  567.     add    [WORD @@DTABuf], ax
  568.     sub    [@@Bytes], ax
  569. ; Read the last sector into Buf2 and copy part of it to DTA
  570. @@LastSector:
  571.     cmp    [@@Bytes], 0
  572.     jz    @@Succeed        ; No bytes left to read
  573.         call    NEAR ReadFileSector, [@@RelSect], 1, [@@FNode], ds (OFFSET Buf2)
  574.     jc      @@ReadError
  575.     mov     cx, [@@Bytes]
  576.     mov     si, OFFSET Buf2
  577.     les     di, [@@DTABuf]
  578.     ASSUME    es:NOTHING
  579.     rep movsb                       ; Transfer bytes to user buffer
  580. ; Done!    
  581. @@Succeed:
  582.     movzx   ecx, [@@OrigBytes]
  583.     mov     bx, [@@SFTOfs]
  584.     add     [fs:bx+15h], ecx
  585.     mov     [@@Result], cx
  586.     clc
  587.     jmp     @@Done
  588. @@SectorNotFound:
  589. @@ReadError:
  590.     mov     [@@Result], errSectorNotFound
  591.     stc
  592. @@Done:
  593.     pop     fs es
  594.     ASSUME    es:NOTHING,fs:NOTHING
  595.     popad
  596.     jc      @@Fail
  597.     mov     cx, [@@Result]
  598.     jmp     @@Exit
  599. @@Fail:
  600.     mov     ax, [@@Result]
  601. @@Exit:
  602.     ret
  603. ENDP    Read
  604.  
  605. ;---------------------------------------------------------------------
  606. ; Write to File
  607. PROC    Write    STDCALL
  608.     stc
  609.     mov     ax, errWriteProt
  610.     ret
  611. ENDP    Write
  612.  
  613. ;---------------------------------------------------------------------
  614. ; Lock Region of File
  615. PROC    LockRegion
  616.     clc
  617.     ret
  618. ENDP    LockRegion
  619.  
  620. ;---------------------------------------------------------------------
  621. ; Unlock Region of File
  622. PROC    UnlockRegion
  623.     clc
  624.     ret
  625. ENDP    UnlockRegion
  626.  
  627. ;---------------------------------------------------------------------
  628. ; Get Disk Space
  629. PROC    DiskFree
  630.     DEFASSUME
  631.     mov    ah, [MediaID]    
  632.     mov    al, 1        ; Sectors per cluster
  633.     mov    ebx, [TotalSectors]    ; Number of clusters
  634.     xor    cl, cl
  635. ; Adjust clusters so that value is 16-bit
  636. @@1:
  637.     cmp    ebx, 0FFFFh    ; Cluster count fits in BX?
  638.     jbe    @@2
  639.     shr    ebx, 1        ; Divide cluster count by 2
  640.     shl    al, 1        ; Multiply sectors per cluster by 2
  641.     inc    cl        ; CL=shift value for sector numbers
  642.     jmp    @@1
  643. @@2:
  644.     mov     edx, [FreeSectors]
  645.     shr    edx, cl        ; Available clusters
  646.     mov     cx, 512         ; Bytes/sector
  647.     clc
  648.     ret
  649. ENDP    DiskFree
  650.  
  651. ;---------------------------------------------------------------------
  652. ; Set File Attributes
  653. PROC    SetAttrib STDCALL
  654.     stc
  655.     mov     ax, errWriteProt
  656.     ret
  657. ENDP    SetAttrib
  658.  
  659. ;---------------------------------------------------------------------
  660. ; Get File Attributes
  661. PROC    GetAttrib STDCALL
  662.     DEFASSUME
  663.     LOCAL   @@Result
  664.     pushad
  665.     push    es
  666.     mov    [BufUsed], 0
  667.     les     di, [FN1]
  668.     call    FindFile
  669.     jc    @@FileNotFound
  670.     mov     al, [Buf1+bx+03h]       ; Extract attributes
  671.     and    al, 10111111b        ; Mask out 8.3 filename bit
  672.     xor     ah, ah
  673.     test    al, 10h            ; Test if subdir
  674.     jz    @@SubDirOK
  675.     call    NEAR ChkPathLength PASCAL, [FN1]
  676.     jnc    @@SubDirOk
  677.     and    al, NOT 10h        ; Turn off subdir attribute
  678. @@SubDirOk:
  679.     mov     [@@Result], ax
  680.     clc
  681.     jmp    @@Done
  682.  
  683. @@FileNotFound:
  684.     mov     [@@Result], errFileNotFound
  685.     stc
  686. @@Done:
  687.     pop     es
  688.     popad
  689.     mov     ax, [@@Result]
  690.     ret
  691. ENDP    GetAttrib
  692.  
  693. ;---------------------------------------------------------------------
  694. ; Rename File
  695. PROC    Rename
  696.     stc
  697.     mov     ax, errWriteProt
  698.     ret
  699. ENDP    Rename
  700.  
  701. ;---------------------------------------------------------------------
  702. ; Delete File
  703. PROC    Delete
  704.     stc
  705.     mov     ax, errWriteProt
  706.     ret
  707. ENDP    Delete
  708.  
  709. ;---------------------------------------------------------------------
  710. ; Open File
  711. PROC    Open    STDCALL
  712.     DEFASSUME
  713.     LOCAL   @@OpenMode:BYTE, @@Result:WORD
  714.     pushad
  715.     push    es fs
  716.     mov     ax, es
  717.     mov     fs, ax
  718.     mov    [BufUsed], 0
  719.     mov     si, di
  720.     les     di, [AccMode]
  721.     mov     al, [es:di]        ; File access mode/sharing
  722.     cmp    [Novell], 0
  723.     jnz    @@OpenModeOk        ; Ignore access mode under Novell DOS
  724.     test    al, 7
  725.     jnz    @@errAccessDenied
  726. @@OpenModeOk:
  727.     mov     [@@OpenMode], al
  728. @@FindFile:
  729.     mov     di, [WORD LOW FN1]      ; First filename buffer
  730.     call    FindFile
  731.     jnc     @@Found
  732.     mov     [@@Result], errFileNotFound
  733.     jmp     @@Fail
  734. @@Found:
  735.     mov     al, [Buf1+bx+03h]       ; Attributes
  736.     and     al, 10h            ; Test subdir attribute
  737.     jnz     @@errAccessDenied
  738.         
  739. ; Set SFT fields
  740.     mov     al, [@@OpenMode]
  741.     and     al, 7Fh
  742.     xor     ah, ah
  743.     mov     [fs:si+02h], ax
  744.     mov     al, [Buf1+bx+03h]       ; Attributes
  745.     and    al, 10111111b        ; Mask out 8.3 filename bit
  746.     mov     [fs:si+04h], al
  747.     mov     ax, 8040h               ; Device info word
  748.     or      al, [DriveNo]           ; Drive number
  749.     mov     [fs:si+05h], ax
  750.     mov     [DWORD fs:si+07h], 0     ; Device driver pointer
  751.     mov     [WORD fs:si+0Bh], 0     ; Cluster #, local files only
  752.     mov     eax, [DWORD Buf1+bx+08h]; Timestamp
  753.     call    Unix2DosTime
  754.     mov     [fs:si+0Dh], eax
  755.     mov     eax, [DWORD Buf1+bx+0Ch] ; File size
  756.     mov     [fs:si+11h], eax
  757.     mov     [DWORD fs:si+15h], 0    ; Current offset in file
  758.     mov     eax, [DWORD Buf1+bx+04h]; FNode sector #
  759.     mov     [fs:si+19h], eax     ; Save in REDIRIFS field
  760. ; Convert filename to FCB format (11 bytes, no dot, blank-padded)
  761. ; Scan to the terminating 0.
  762.     cld
  763.     xor     al, al
  764.     mov     cx, -1
  765.     repne scasb
  766. ; Scan back to the last \
  767.     std
  768.     mov     al, '\'
  769.     mov     cx, -1
  770.     repne   scasb
  771.     add     di, 2
  772.  
  773.     xchg    si, di
  774.     push    es
  775.     push    fs
  776.     pop     es
  777.     pop     ds
  778.     ASSUME    ds:NOTHING
  779.     add     di, 20h
  780.  
  781.     mov     cx, 11
  782.     mov     al, ' '
  783.     cld
  784.     rep stosb                       ; Clear the field
  785.     sub     di, 11
  786.     lea     dx, [di+8]              ; Extension part
  787. @@Fn1:  lodsb
  788.     or      al, al
  789.     je      @@Succeed
  790.     cmp     al, '.'
  791.     jne     @@Fn2
  792.     mov     di, dx                  ; Move on to extension field if '.'
  793.     jmp     @@Fn1
  794. @@Fn2:
  795.     stosb
  796.     jmp     @@Fn1
  797.  
  798. @@errAccessDenied:
  799.     mov     [@@Result], errAccessDenied
  800.     jmp     @@Fail
  801. @@Succeed:      
  802.     clc
  803.     jmp     @@Done
  804. @@Fail: stc
  805. @@Done: pop     fs es
  806.     popad
  807.     jnc     @@Exit
  808.     mov     ax, [@@Result]
  809. @@Exit:
  810.     ret
  811. ENDP    Open
  812.  
  813. ;---------------------------------------------------------------------
  814. ; Create File
  815. PROC    Create
  816.     stc
  817.     mov     ax, errWriteProt
  818.     ret
  819. ENDP    Create
  820.  
  821. ;---------------------------------------------------------------------
  822. ; Find First Matching File
  823. PROC    FindFirst STDCALL
  824.     DEFASSUME
  825.     LOCAL   @@Result, @@SrchTmplPos, @@DirFNode:DWORD, @@Attr:BYTE
  826.     LOCAL    @@LongPath:BYTE, @@DTA:DWORD
  827.     pushad
  828.     push    es
  829.     mov    ax, cs
  830. ; Move the filename from SDA First Filename buffer to FNameBuf2
  831.     mov    [BufUsed], 0
  832.     mov    [@@LongPath], 0        ; Set if pathname is long
  833.     lds    si, [pDTA]
  834.     ASSUME    ds:NOTHING
  835.     mov    ecx, [si]
  836.     mov    [@@DTA], ecx        ; Address of DTA
  837.     lds     si, [FN1]
  838.     mov    es, ax
  839.     ASSUME    es:ResCode
  840.     mov     di, OFFSET FNameBuf2
  841.     mov     cx, 32
  842.     cld
  843.     rep movsd
  844.     mov     ds, ax
  845.     ASSUME    ds:ResCode
  846. ; Find the last \ and replace it with 0
  847.     xor     al, al
  848.     mov     cx, 128
  849.     mov     di, OFFSET FNameBuf2
  850.     repne scasb
  851.     jne     @@PathNotFound
  852.     dec     di
  853.     mov     al, '\'
  854.     sub     cx, 128
  855.     neg     cx
  856.     std
  857.     repne scasb
  858.     jne     @@PathNotFound
  859.     inc     di
  860.     mov     [BYTE di], 0
  861.     inc     di
  862.     mov     [@@SrchTmplPos], di       ; Where the search template starts
  863.     mov    ax, di
  864.     sub    ax, OFFSET FNameBuf2
  865.     cmp    ax, MaxPathLength-8    ; Long path?
  866.     seta    [@@LongPath]
  867.     ror    [@@LongPath], 1        ; Move flag to high bit
  868. ; Find the directory
  869.     cmp     di, OFFSET FNameBuf2+3  ; See if it's in the root dir.
  870.     jne     @@FindDir
  871.     mov     ecx, [RootFNode]
  872.     mov     [@@DirFNode], ecx
  873.     jmp     @@DirOk
  874. @@FindDir:
  875.     mov     di, OFFSET FNameBuf2
  876.     call    FindFile
  877.     jc      @@PathNotFound
  878. ; Check that it's really a directory.
  879.     test    [BYTE Buf1+bx+03], 10h
  880.     jz      @@PathNotFound
  881.     mov     ecx, [DWORD Buf1+bx+04h]    ; FNode of directory
  882.     mov     [@@DirFNode], ecx
  883.  
  884. @@DirOk:
  885.     les     di, [SrchAttr]
  886.     ASSUME    es:NOTHING
  887.     mov     al, [es:di]            ; Search attribute
  888.     mov     [@@Attr], al
  889. ; Initialize the search data block in the DTA.
  890. ; The "drive number" byte does not seem to work as specified in Windows 95.
  891. ; We later overwrite this with a magic value that seems to work. The reason
  892. ; for this behaviour is unknown.
  893.     cld
  894.     les    di, [@@DTA]
  895.     mov     al, [DriveNo]           ; Drive number
  896.     or      al, 80h                 ; Set bit 7 for remote drive
  897.     stosb
  898. ; Search template - 11 bytes, padded with spaces.
  899.     mov     cx, 11
  900.     mov     al, ' '
  901.     rep stosb                       ; Clear the field
  902.     sub     di, 11
  903.     lea     dx, [di+8]              ; Extension part
  904.     mov     si, [@@SrchTmplPos]
  905. @@1:    lodsb
  906.     or      al, al
  907.     je      @@TemplateDone
  908.     cmp     al, '.'
  909.     jne     @@2
  910.     mov     di, dx                  ; Move on to extension field if '.'
  911.     jmp     @@1
  912. @@2:
  913.     stosb
  914.     jmp     @@1
  915.  
  916. @@TemplateDone:
  917.     mov     di, dx
  918.     add     di, 3
  919.     mov     al, [@@Attr]
  920.     stosb
  921.  
  922. ; Read the directory FNode
  923.     call    NEAR ReadSector, [@@DirFNode], 1, ds (OFFSET Buf1)
  924.     jc      @@NoMoreFiles
  925.     mov     eax, [DWORD Buf1+48h] ; Starting sector
  926.     les     bx, [@@DTA]
  927.     mov     [es:bx+0Dh], eax     ; Save in SDB
  928.     xor    al, al
  929.     mov    ah, [@@LongPath]
  930.     mov     [WORD es:bx+11h], ax ; Offset of last entry + long path flag
  931.  
  932.     test    [@@Attr], 08h                 ; Volume label?
  933.     jz      @@RegularFile
  934. ; Volume label
  935.     push    ds
  936.     pop     es
  937.     ASSUME    es:ResCode
  938.     mov    ds, [ResDataSeg]
  939.     ASSUME    ds:ResData
  940. ; Move volume label to FNameBuf
  941.     mov     di, OFFSET FNameBuf
  942.     mov     cx, 8
  943.     mov     si, OFFSET Volabel
  944.     rep movsb
  945.     mov     al, '.'
  946.     stosb
  947.     mov     cx, 3
  948.     rep movsb
  949.     push    cs
  950.         pop    ds
  951.     ASSUME    ds:ResCode
  952.  
  953.     mov     dx, OFFSET FNameBuf
  954.     mov     bx, [@@SrchTmplPos]
  955.     call    Match
  956.     jnc    @@DoVolLabel
  957.     cmp    [@@Attr], 08h        ; Only volume label?
  958.     je    @@NoMoreFiles
  959.     jmp    @@RegularFile
  960. @@DoVolLabel:
  961. ; Set the directory entry for found vol. label.
  962.     les     di, [@@DTA]
  963.     ASSUME    es:NOTHING
  964.     add     di, 15h            ; Directory entry for found file
  965.     mov     cx, 11
  966.     mov     al, ' '
  967.     cld
  968.     rep stosb
  969.     sub     di, 11
  970.     lea     dx, [di+11]
  971.     mov     cx, 11
  972.     mov     si, OFFSET Volabel
  973.     mov    ds, [ResDataSeg]
  974.     ASSUME    ds:ResData
  975. @@MoveVolabel:
  976.     lodsb
  977.     or      al, al
  978.     jz      @@VolabelDone
  979.     stosb
  980.     loop    @@MoveVolabel
  981. @@VolabelDone:
  982.     push    cs
  983.         pop    ds
  984.     ASSUME    ds:ResCode
  985.     mov     di, dx
  986.     mov     al, 08h
  987.     stosb                           ; Attributes
  988.     add     di, 10
  989.     xor     eax, eax
  990.     stosd                           ; Time and date
  991.     stosw                           ; Starting cluster
  992.     stosd                           ; File size     
  993.     clc
  994.     jmp     @@Done
  995.  
  996. @@RegularFile:
  997. ; Call FindNext to do the actual directory search
  998.     les    di, [@@DTA]
  999.     call    FindNext
  1000.     mov     [@@Result], ax
  1001.     jmp     @@Done
  1002.  
  1003. @@PathNotFound:
  1004.     stc
  1005.     mov     [@@Result], errPathNotFound
  1006.     jmp     @@Done
  1007. @@NoMoreFiles:
  1008.     stc
  1009.     mov     [@@Result], errNoMoreFiles
  1010.     jmp     @@Done
  1011. @@Done:
  1012.     pop     es
  1013.     popad
  1014.     jnc     @@Exit
  1015.     mov     ax, [@@Result]
  1016. @@Exit:
  1017.     ret
  1018. ENDP    FindFirst
  1019.  
  1020. ;---------------------------------------------------------------------
  1021. ; Find Next Matching File
  1022. ; Use of reserved or unused SDB fields:
  1023. ; Offset Size    Function
  1024. ; 0Dh     DWORD    First sector of the directory block last searched.
  1025. ; 11h     WORD    Bit 15   : Set if subdirs are not to be returned (long paths)
  1026. ;        Bits 0-14: Offset into directory block of the last entry 
  1027. ;        examined, or 0=no last entry, 1=only "." entry returned.
  1028. PROC    FindNext STDCALL
  1029.     DEFASSUME
  1030.     LOCAL   @@Result, @@DirBlock:DWORD, @@LastEntry, @@Root:BYTE, @@NoSubDirs:BYTE
  1031.     LOCAL    @@FileAttr:BYTE, @@FileSize:DWORD, @@DTA:DWORD
  1032.     pushad
  1033.     push    es
  1034.     cld
  1035.     mov    [@@Root], 0        ; Flag is set if root dir.
  1036.     les    bx, [pDTA]
  1037.     mov    eax, [es:bx]
  1038.     mov    [@@DTA], eax        ; Address of DTA
  1039.     les     bx, [es:bx]        ; ES:BX -> DTA
  1040.     mov     eax, [es:bx+0Dh]    ; SDB, directory block.
  1041.     mov     [@@DirBlock], eax
  1042.     mov     ax, [es:bx+11h]        ; SDB, offset of last entry
  1043.     mov    [@@NoSubDirs], ah    ; Copy to subdirectory flag
  1044.     and    [@@NoSubDirs], 80h
  1045.     and    ah, 7Fh
  1046.     mov     [@@LastEntry], ax
  1047. @@Search:
  1048.     cmp    [BufUsed], 0
  1049.     jnz    @@GotDirBlock
  1050.     call    NEAR ReadSector, [@@DirBlock], 4, ds (OFFSET Buf1)
  1051.     jc      @@NoMoreFiles
  1052. @@GotDirBlock:
  1053.     mov    [BufUsed], 0
  1054. ; See if we're in the root dir.
  1055.     mov    eax, [DWORD Buf1+0Ch]
  1056.     cmp    eax, [RootFNode]
  1057.     jne    @@TransferTemplate
  1058.     mov    [@@Root], 1        ; Set root dir flag.
  1059. ; Transfer search template to ASCIIZ format in FNameBuf2
  1060. @@TransferTemplate:
  1061.     push    ds
  1062.     pop     es
  1063.     lds     si, [@@DTA]
  1064.     ASSUME    ds:NOTHING, es:ResCode
  1065.     inc    si                ; SDB Search template
  1066.     mov     di, OFFSET FNameBuf2
  1067.     mov     cx, 8
  1068. @@MoveTmpl1:
  1069.     lodsb
  1070.     cmp     al, ' '                         ; Go to extension if blank
  1071.     je      @@MoveTmplExt
  1072.     stosb
  1073.     loop    @@MoveTmpl1
  1074.     inc     si
  1075. @@MoveTmplExt:
  1076.     add     si, cx                          ; Extension field in template
  1077.     dec     si
  1078.     mov     cx, 3
  1079.     mov     al, '.'
  1080.     stosb
  1081. @@MoveTmpl2:
  1082.     lodsb
  1083.     cmp     al, ' '
  1084.     je      @@MoveTmplDone
  1085.     stosb
  1086.     loop    @@MoveTmpl2
  1087. @@MoveTmplDone:
  1088.     xor     al, al                          ; Zero terminate
  1089.     stosb
  1090.     mov    ax, cs
  1091.     mov    ds, ax
  1092.     mov    es, ax
  1093.     ASSUME    ds:ResCode, es:NOTHING
  1094.  
  1095.     mov     bx, [@@LastEntry]
  1096.     cmp    bx, 1
  1097.     ja    @@NextEntry
  1098.     je    @@DotDot
  1099.     mov     bx, 14h                 ; Offset of first dir. entry
  1100.     jmp     @@DoEntry
  1101.  
  1102. @@NextEntry:    
  1103.     test    [BYTE Buf1+bx+02h], 08h ; Last entry in block?
  1104.     jz      @@MoveToNextEntry
  1105. ; Read higher level directory block.
  1106.     call    NEAR ReadSector, [DWORD Buf1+0Ch], 4, ds (OFFSET Buf1)
  1107.     jc    @@NoMoreFiles
  1108. ; Check that it's a directory sector, not the FNode.
  1109.     cmp     [BYTE Buf1+03h], 0F7h      ; FNode signature
  1110.     je      @@NoMoreFiles
  1111. ; Go through this directory block to find the entry that we came from.
  1112.     mov     edx, [@@DirBlock]
  1113.     mov     bx, 14h
  1114. @@SrchParent:
  1115.     test    [Buf1+bx+02h], 04h      ; Entry has B tree pointer?
  1116.     jz      @@NotParent
  1117.     mov     si, bx
  1118.     add     si, [WORD Buf1+bx]
  1119.     cmp     [DWORD Buf1+si-4], edx
  1120.     je      @@FoundParent
  1121. @@NotParent:    
  1122.     test    [Buf1+bx+02h], 08h      ; Last entry in block?
  1123.     jnz     @@NoMoreFiles
  1124.     add     bx, [WORD Buf1+bx]
  1125.     jmp     @@SrchParent
  1126.  
  1127. @@FoundParent:
  1128.     mov     eax, [DWORD Buf1+10h]               ; Sector number
  1129.     mov     [@@DirBlock], eax
  1130.     mov     [@@LastEntry], bx 
  1131.     jmp     @@NoBTree
  1132.  
  1133. @@MoveToNextEntry:
  1134.     add     bx, [WORD Buf1+bx]  ; Move to next entry
  1135. @@DoEntry:
  1136. ; Check if entry has a B Tree pointer
  1137.     mov     [@@LastEntry], bx 
  1138.     test    [BYTE Buf1+bx+02h], 04h
  1139.     jz      @@NoBTree
  1140. ; Go down the branch
  1141.     add     bx, [WORD Buf1+bx]
  1142.     mov     eax, [DWORD Buf1+bx-04h]    ; B Tree pointer
  1143.     mov     [@@DirBlock], eax
  1144.     mov     [@@LastEntry], 0
  1145.     jmp     @@Search
  1146.  
  1147. ; No B Tree, check this entry for a match.
  1148. @@NoBTree:
  1149.     test    [BYTE Buf1+bx+02h], 08h     ; Last entry in block?
  1150.     jnz     @@NextEntry
  1151.     les     di, [@@DTA]
  1152.     ASSUME    es:NOTHING
  1153. ; Match file attributes with AT MOST the specified combination of srch attrib.
  1154.     mov    ah, [Buf1+bx+03h]        ; File attributes
  1155.     cmp    [@@NoSubDirs], 0        ; Allowed to return subdirs?
  1156.     jz    @@SubDirOk
  1157.     and    ah, NOT 10h            ; Subdirectory returned as file
  1158. @@SubDirOk:
  1159.     mov     al, [es:di+0Ch]                 ; Search attributes
  1160.     not     al
  1161.     and     al, ah                ; Compare attributes
  1162.     and     al, 10011110b                   ; Ignore bits 0, 5 and 6
  1163.     jnz     @@NextEntry
  1164.     push    cs
  1165.     pop    es
  1166.     ASSUME    es:ResCode
  1167. ; Check if "." entry.
  1168.     test    [BYTE Buf1+bx+02h], 01h
  1169.     jz    @@NoDot
  1170.     cmp    [@@Root], 0            ; No "." in root.
  1171.     jnz    @@NoDot
  1172. ; Return . file if it matches filespec. 
  1173.     mov    si, OFFSET FNameBuf2
  1174.     call    MatchDot
  1175.     jc    @@NoDot
  1176.     les     di, [@@DTA]
  1177.     ASSUME    es:NOTHING
  1178. ; Write directory entry for . file
  1179.     mov     eax, [@@DirBlock]
  1180.         mov     [BYTE es:di], 0d2h        ; Win95 kludge
  1181.     mov     [es:di+0Dh], eax               ; Save current dir block
  1182.     mov     ax, 01h                ; Flag "." file returned
  1183.     or    ah, [@@NoSubDirs]        ; Keep subdir flag bit
  1184.     mov     [es:di+11h], ax                ; Save offset of found entry
  1185.     add     di, 15h                      ; Point to found file field
  1186. ; Transfer the filename
  1187.     mov    eax, '   .'
  1188.     stosd
  1189.     mov    eax, '    '
  1190.     stosd
  1191.     stosw
  1192.     stosb
  1193.     mov     al, [Buf1+14h+03h]               ; Attributes
  1194.     stosb
  1195.     add     di, 10                           ; Reserved field
  1196.     lea     si, [Buf1+14h+08h]               ; Timestamp field
  1197.     lodsd
  1198.     call    Unix2DosTime
  1199.     stosd
  1200.     xor     ax, ax
  1201.     stosw                                   ; Cluster #
  1202.     movsd                                   ; File size
  1203.     mov    [BufUsed], 1
  1204.     clc
  1205.     jmp     @@Done
  1206.  
  1207. ; Return ".." if attributes match
  1208. @@DotDot:
  1209.     mov    bx, 14h
  1210.     mov    si, OFFSET FNameBuf2
  1211.     call    MatchDotDot
  1212.     jc    @@NextEntry
  1213.     les     di, [@@DTA]
  1214.     ASSUME    es:NOTHING
  1215. ; Match file attributes with AT MOST the specified combination of srch attrib.
  1216.     mov     al, [es:di+0Ch]                 ; Search attributes
  1217.     not     al
  1218.     and     al, [Buf1+14h+03h]              ; File attributes
  1219.     and     al, 10011110b                   ; Ignore bits 0, 5 and 6
  1220.     jnz     @@NextEntry
  1221. ; Write directory entry for .. file
  1222.     mov     eax, [@@DirBlock]
  1223.         mov     [BYTE es:di], 0d2h        ; Win95 kludge
  1224.     mov     [es:di+0Dh], eax               ; Save current dir block
  1225.     mov     ax, 14h                ; First entry completed
  1226.     or    ah, [@@NoSubDirs]
  1227.     mov     [es:di+11h], ax                ; Save offset of found entry
  1228.     add     di, 15h                      ; Point to found file field
  1229. ; Transfer the filename
  1230.     mov    eax, '  ..'
  1231.     stosd
  1232.     mov    eax, '    '
  1233.     stosd
  1234.     stosw
  1235.     stosb
  1236.     mov     al, [Buf1+14h+03h]               ; Attributes
  1237.     stosb
  1238.     add     di, 10                           ; Reserved field
  1239.     lea     si, [Buf1+14h+08h]               ; Timestamp field
  1240.     lodsd
  1241.     call    Unix2DosTime
  1242.     stosd
  1243.     xor     ax, ax
  1244.     stosw                                   ; Cluster #
  1245.     movsd                                   ; File size
  1246.     mov    [BufUsed], 1
  1247.     clc
  1248.     jmp     @@Done
  1249.  
  1250.     ASSUME    es:ResCode
  1251. @@NoDot:
  1252. ; See if filename is valid in DOS.
  1253.     test    [BYTE Buf1+bx+03h], 40h
  1254.     jz    @@MoveFileName
  1255. ; Convert long filenames?
  1256.     cmp    [ConvertLong], 0
  1257.     je    @@NextEntry
  1258. ; Convert long filename to valid name in FNameBuf
  1259.     lea    si, [Buf1+bx+1Fh]    
  1260.     mov    di, OFFSET FNameBuf
  1261.     movzx    cx, [BYTE si-1]
  1262.     call    ConvertFilename
  1263.     jmp    @@MatchFilename    
  1264.  
  1265. ; Move filename to FNameBuf, converting it to upper case
  1266. @@MoveFilename:
  1267.     lea     si, [Buf1+bx+1Fh]
  1268.     mov     di, OFFSET FNameBuf
  1269.     mov     cl, [Buf1+bx+1Eh]
  1270.     xor     ch, ch
  1271. @@MoveChar:
  1272.     lodsb
  1273.     call    UpCase
  1274.     stosb
  1275.     loop    @@MoveChar
  1276.     xor     al, al
  1277.     stosb                                   ; Zero terminate
  1278. ; Check if filename matches search template
  1279. @@MatchFilename:
  1280.     mov     dx, OFFSET FNameBuf             ; Filename
  1281.     push    bx
  1282.     mov     bx, OFFSET FNameBuf2            ; Search template, ASCIIZ
  1283.     call    Match
  1284.     pop     bx
  1285.     jc      @@NextEntry
  1286. ; Match found - set directory entry
  1287.     les     di, [@@DTA]
  1288.     ASSUME    es:NOTHING
  1289.         mov     [BYTE es:di], 0d2h        ; Win95 kludge
  1290.     mov     eax, [@@DirBlock]
  1291.     mov     [es:di+0Dh], eax           ; Save current dir block
  1292.     mov     ax, [@@LastEntry]
  1293.     or    ah, [@@NoSubDirs]
  1294.     mov     [es:di+11h], ax            ; Save offset of found entry
  1295.     add     di, 15h                    ; Point to found file field
  1296. ; Pad with spaces.
  1297.     push    di
  1298.     mov     cx, 11
  1299.     mov     al, ' '
  1300.     rep stosb
  1301.     pop     di
  1302. ; Transfer the filename
  1303.     mov     si, OFFSET FNameBuf
  1304.     mov     cx, 9
  1305.     lea     dx, [di+8]
  1306. @@Tf1:
  1307.     lodsb
  1308.     or      al, al
  1309.     je      @@TfDone
  1310.     cmp     al, '.'
  1311.     je      @@TfExt
  1312.     stosb
  1313.     loop    @@Tf1
  1314.  
  1315. ; Transfer the extension
  1316. @@TfExt:
  1317.     mov     cx, 3
  1318.     mov     di, dx
  1319. @@TfExt1:
  1320.     lodsb
  1321.     call    UpCase
  1322.     or      al, al
  1323.     je      @@TfDone
  1324.     stosb
  1325.     loop    @@TfExt1
  1326. @@TfDone:
  1327.     mov     di, dx
  1328.     add     di, 3
  1329.     mov     al, [Buf1+bx+03h]               ; Attributes
  1330.     and    al, 10111111b            ; Mask out 8.3 filename bit
  1331.     cmp    [@@NoSubDirs], 0        ; Allowed to return subdir?
  1332.     jz    @@AttrOk
  1333.     and    al, NOT 10h            ; Return subdir as file
  1334. @@AttrOk:
  1335.     stosb
  1336.     add     di, 10                          ; Reserved field
  1337.     lea     si, [Buf1+bx+08h]               ; Timestamp field
  1338.     lodsd
  1339.     call    Unix2DosTime
  1340.     stosd
  1341.     xor     ax, ax
  1342.     stosw                                   ; Cluster #
  1343.     movsd                    ; File size
  1344.     mov    [BufUsed], 1
  1345.     clc
  1346.     jmp     @@Done
  1347.  
  1348. @@NoMoreFiles:
  1349.     stc
  1350.     mov     [@@Result], errNoMoreFiles
  1351. @@Done:
  1352.     pop     es
  1353.     popad
  1354.     jnc     @@Exit
  1355.     mov     ax, [@@Result]
  1356. @@Exit:
  1357.     ret
  1358. ENDP    FindNext
  1359.  
  1360. ;---------------------------------------------------------------------
  1361. ; Seek from End of File
  1362. ; NOTE Seems like this function never gets called! DOS changes the
  1363. ; file pointers itself.
  1364. PROC    Seek    STDCALL
  1365.     DEFASSUME
  1366.     LOCAL   @@RetValue:DWORD
  1367.     pushad
  1368.     cmp     [WORD es:di], 0     ; Open files count
  1369.     jne     @@SFTOk
  1370.     mov     [WORD @@RetValue], errInvalidHandle
  1371.     stc
  1372.     jmp     @@Done
  1373. @@SFTOk:
  1374.     shl     ecx, 16
  1375.     mov     cx, dx          ; Offset from end of file in ECX
  1376.     mov     eax, [es:di+11h] ; File size
  1377.  
  1378.     sub     eax, ecx
  1379.     jnc     @@1
  1380.     xor     eax, eax
  1381. @@1:
  1382.     mov     [es:di+15h], eax ; New file pos.
  1383.     mov     [@@RetValue], eax
  1384.     clc
  1385. @@Done:
  1386.     popad
  1387.     jnc     @@2
  1388.     mov     ax, [WORD @@RetValue]
  1389.     jmp     @@Exit
  1390. @@2:    mov     ax, [WORD @@RetValue]
  1391.     mov     dx, [WORD @@RetValue+2]
  1392. @@Exit:
  1393.     ret
  1394. ENDP    Seek
  1395.  
  1396. ;---------------------------------------------------------------------
  1397. ; Extended Open File
  1398. ; Never gets called under DR-DOS 6.0 which doesn't support extended open.
  1399. PROC    ExtOpen STDCALL
  1400.     DEFASSUME
  1401.     LOCAL   @@OpenMode, @@RetValue
  1402.     pushad
  1403.     push    fs
  1404.     lfs     si, [ExtOpenMode]
  1405.     mov     ax, [fs:si]                    ; Open mode
  1406.     mov    bx, [WORD LOW AccMode]        ; Exchange with normal open mode
  1407.     xchg    [fs:bx], ax
  1408.     mov     [@@OpenMode], ax
  1409.     call    Open
  1410.     mov     [@@RetValue], ax
  1411.     mov     ax, [@@OpenMode]
  1412.     mov     [fs:bx], ax
  1413.         jc      @@Fail
  1414.     mov     [@@RetValue], 01h                 ; File opened
  1415.     jmp     @@Done
  1416.  
  1417. @@Fail:
  1418. @@Done:
  1419.     pop     fs
  1420.     popad
  1421.     jnc     @@Succeeded
  1422.     mov     ax, [@@RetValue]
  1423.     jmp     @@Exit
  1424. @@Succeeded:
  1425.     mov     cx, [@@RetValue]
  1426. @@Exit:
  1427.     ret
  1428. ENDP    ExtOpen
  1429.  
  1430. ;------------------------------------------------------------------------
  1431. ; Convert logical sector number to Cyl/Head/Sect in CX, DX, and AL
  1432. ; as passed to INT 13h. Logical sector passed in ECX.
  1433. PROC    LogToCHS
  1434.     add    ecx, [LBAStart]
  1435.     movzx   ax, [nSecs]
  1436.     mul    [nHeads]
  1437.     mov     bx, ax          ; Sectors/track * heads
  1438.     mov     ax, cx
  1439.     mov     edx, ecx
  1440.     shr     edx, 16         ; DX:AX = logical sector #
  1441.     div     bx
  1442.     push    ax              ; Cylinder
  1443.     mov     ax, dx
  1444.     div     [nSecs]
  1445.     movzx   dx, al          ; Head
  1446.     mov     cl, ah          ; Sector
  1447.     inc    cl
  1448.     pop     ax              ; Cylinder
  1449.     mov    dh, dl        ; Head
  1450.     mov     ch, al
  1451.     xor     al, al
  1452.     shr     ax, 2
  1453.     or      cl, al          ; bits 8 and 9 of cyl. number go here
  1454.         xor     al, al
  1455.         shr     ax, 2
  1456.         or      dh, al          ; bits 10 and 11 of cyl (BIOS extension)
  1457.     ret
  1458. ENDP    LogToCHS
  1459.  
  1460. ;------------------------------------------------------------------------
  1461. ; Read sectors from disk.
  1462. PROC    DiskRead PASCAL
  1463.     DEFASSUME
  1464.     ARG    @@Sector:DWORD, @@NumSectors:WORD, @@Dest:DWORD
  1465.     LOCAL    @@CurrSector:DWORD, @@SectorsToRead:WORD, @@CurrDest:DWORD
  1466.     pushad
  1467.     push    es
  1468.     mov    eax, [@@Sector]
  1469.     mov    [@@CurrSector], eax
  1470.     add    eax, [LBAstart]
  1471.     mov    [DWORD LOW DiskAddrPkt.Sector], eax
  1472.     mov    ax, [@@NumSectors]
  1473.     mov    [@@SectorsToRead], ax
  1474.     mov    [DiskAddrPkt.Count], ax
  1475.     mov    eax, [@@Dest]
  1476.     mov    [@@CurrDest], eax
  1477.     mov    [DiskAddrPkt.Buffer], eax
  1478.     cmp    [AccessMethod], Method_Ext
  1479.     jne    @@ReadCHS
  1480. ; Read using LBA addressing
  1481.     mov    dl, [PhysDrv]
  1482.     mov    si, OFFSET DiskAddrPkt
  1483.     mov    ah, 42h
  1484.     int    13h
  1485.     jc    @@Done
  1486.     jmp    @@CopyToCache
  1487.  
  1488. ; Read using CHS addressing
  1489. @@ReadCHS:
  1490.     mov     ecx, [@@CurrSector]
  1491.     call    LogToCHS
  1492.         mov     al, [BYTE @@NumSectors]
  1493.         cmp     [Multitrack], 0
  1494.         jnz     @@DoInt13
  1495. ; Multitrack operations not supported
  1496.     push    cx
  1497.     and    cl, 3Fh
  1498.     mov    al, [nSecs]
  1499.     sub    al, cl
  1500.     inc    al        ; Sectors left on this track
  1501.     xor    ah, ah
  1502.     cmp    ax, [@@SectorsToRead]
  1503.     jbe    @@3
  1504.     mov    al, [BYTE @@SectorsToRead] ; Read SectorsToRead sectors
  1505. @@3:    pop    cx
  1506. @@DoInt13:
  1507.         mov     dl, [PhysDrv]
  1508.     mov     ah, 02h
  1509.     les     bx, [@@CurrDest]
  1510.     push    ax
  1511.     int     13h
  1512.     pop    ax
  1513.     jc      @@Done
  1514.     and    eax, 0FFh
  1515.     les    di, [@@CurrDest]    ; Buffer
  1516.     movzx    cx, al            ; Number of sectors
  1517.     mov    edx, [@@CurrSector]    ; First sector
  1518.     add    [@@CurrSector], eax
  1519.     sub    [@@SectorsToRead], ax
  1520.     push    dx
  1521.     mov    dx, 200h
  1522.     mul    dx
  1523.     pop    dx
  1524.     add    [WORD @@CurrDest], ax
  1525. ; Check if any more sectors to read
  1526. @@SectorsLeft:
  1527.     cmp    [@@SectorsToRead], 0
  1528.     jnz    @@ReadCHS
  1529. ; Copy read sectors to cache.
  1530. @@CopyToCache:
  1531.     cmp    [CacheOn], 0
  1532.     jz    @@Done
  1533.     cmp    [XMSError], 0
  1534.     jnz    @@Done
  1535.     mov    edx, [@@Sector]
  1536.     les    di, [@@Dest]
  1537.     mov    cx, [@@NumSectors]
  1538. @@CacheLoop:
  1539.     call    GetFreeCacheEntry    ; Get free cache entry in BX.
  1540.     or    bx, bx
  1541.     sete    [XMSError]
  1542.     jz    @@Done
  1543.     push    cx
  1544.     mov    ecx, edx
  1545.     call    CacheInsert        ; Insert sector ECX at ES:DI at entry BX.
  1546.     pop    cx
  1547.     add    edx, 1
  1548.     add    di, 200h
  1549.     loop    @@CacheLoop
  1550.     clc
  1551. @@Done:
  1552.     pop    es
  1553.     popad
  1554.     ret
  1555. ENDP    DiskRead
  1556.  
  1557. ;------------------------------------------------------------------------
  1558. ; Read logical sectors into memory.
  1559. PROC    ReadSector PASCAL
  1560.     DEFASSUME
  1561.     ARG    @@Sector:DWORD, @@NumSectors:WORD, @@Dest:DWORD
  1562.     LOCAL   @@SectorsToRead
  1563.     pushad
  1564.     push    es
  1565. @@Start:
  1566.     mov    dx, [@@NumSectors]
  1567.     mov    [@@SectorsToRead], dx
  1568.     cmp     [CacheOn], 0
  1569.     jz      @@DiskRead
  1570.     cmp     [XMSError], 0
  1571.     jnz     @@DiskRead
  1572. @@SearchCache:
  1573.     mov    ecx, [@@Sector]
  1574.     call    SearchCache        ; Search for sector ECX in cache
  1575.     or    bx, bx
  1576.     jz    @@CacheMiss
  1577. ; Cache hit. Move the sector to the destination.
  1578. @@CacheHit:
  1579.     les    di, [@@Dest]
  1580.     call    ReadCacheSector        ; Read sector in entry BX
  1581.     or    ax, ax
  1582.     sete    [XMSError]
  1583.     jz    @@DiskRead
  1584.     inc    [@@Sector]
  1585.     add    [WORD @@Dest], 200h
  1586.     sub    [@@NumSectors], 1        ; Sectors left to read?
  1587.     jnz    @@SearchCache
  1588.     jmp     @@Done
  1589.  
  1590. @@CacheMiss:
  1591. ; Determine number of consecutive sectors to read from disk.
  1592.     mov    ecx, [@@Sector]
  1593.     mov    [@@SectorsToRead], 0
  1594. @@ConsecutiveSectors:
  1595.     inc    ecx
  1596.     inc    [@@SectorsToRead]
  1597.     mov    ax, [@@NumSectors]
  1598.     cmp    ax, [@@SectorsToRead]
  1599.     jbe    @@DiskRead
  1600.     push    ecx
  1601.     call    SearchCache
  1602.     pop    ecx
  1603.     or    bx, bx
  1604.     jz    @@ConsecutiveSectors
  1605.  
  1606. ; Read sectors from disk.
  1607. @@DiskRead:
  1608.     movzx    eax, [@@SectorsToRead]
  1609.     call    NEAR DiskRead, [@@Sector], ax, [@@Dest]
  1610.     jc    @@Done
  1611.     add    [@@Sector], eax
  1612.     sub    [@@NumSectors], ax
  1613.     jz    @@Done        ; No sectors left to read
  1614.     mov    dx, 200h
  1615.     mul    dx
  1616.     add    [WORD @@Dest], ax    
  1617.     jmp     @@Start
  1618.  
  1619. @@Done:
  1620.     pop     es
  1621.     popad
  1622.     ret
  1623. ENDP    ReadSector
  1624.  
  1625. ;---------------------------------------------------------------------
  1626. ; Inserts a sector into the cache at a free entry. Sector number ECX,
  1627. ; entry number BX, memory address ES:DI. Returns CF=1 on XMS error.
  1628. PROC    CacheInsert STDCALL
  1629.     LOCAL    @@EntryNum, @@SectorAddr:DWORD, @@Sector:DWORD
  1630.     DEFASSUME
  1631.         pushad
  1632.     mov     si, OFFSET XMoveStruc
  1633.     mov    [WORD @@SectorAddr], di
  1634.     mov    [WORD @@SectorAddr+2], es
  1635.     mov    [@@Sector], ecx
  1636.     mov    [@@EntryNum], bx
  1637. ; Store the sector number
  1638.     mov     eax, [@@Sector]
  1639.     mov     [DWORD EntryBuf], eax
  1640.     mov    [WORD EntryBuf+0Ch], gs        ; Data segment
  1641. ; Insert the new sector at the head of the priority list.
  1642. ; next[x] <- next[sentinel]
  1643.     mov     [XMoveStruc.Length], 2
  1644.     mov     ax, [hCacheLists]
  1645.     mov     [XMoveStruc.SourceHandle], ax
  1646.     mov     [XMoveStruc.DestHandle], 0
  1647.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+0Ah
  1648.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  1649.     mov     [XMoveStruc.SourceOffset], 0Ah  ; Next field of sentinel
  1650.     mov     ah, 0Bh
  1651.     call    [XMSEntry]
  1652.     or      ax, ax
  1653.     jz      @@XMSError
  1654. ; prev[next[sentinel]] <- x
  1655.     mov     ax, [WORD EntryBuf+0Ah]
  1656.     mov     dx, CacheEntrySize
  1657.     mul     dx
  1658.     add     ax, 08h
  1659.     adc     dx, 0
  1660.     mov     [WORD XMoveStruc.DestOffset], ax
  1661.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1662.     mov     ax, [hCacheLists]
  1663.     mov     [XMoveStruc.DestHandle], ax
  1664.     mov     [XMoveStruc.SourceHandle], 0
  1665.     mov    bx, [@@EntryNum]
  1666.     mov     [WORD EntryBuf+CacheEntrySize], bx
  1667.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
  1668.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1669.     mov     ah, 0Bh
  1670.     call    [XMSEntry]
  1671.     or      ax, ax
  1672.     jz      @@XMSError
  1673. ; next[sentinel] <- x
  1674.     mov     [XMoveStruc.DestOffset], 0Ah
  1675.     mov     ah, 0Bh
  1676.     call    [XMSEntry]
  1677.     or      ax, ax
  1678.     jz      @@XMSError
  1679. ; prev[x] <- sentinel
  1680.     mov     [EntryBuf+08h], 0
  1681. ; Compute hash function
  1682.     mov     eax, [@@Sector]
  1683.     mov     edx, eax
  1684.     shr     edx, 16         ; Sector in DX:AX
  1685.     div     [HashSize]      ; Hash slot in DX
  1686.     shl     edx, 1          ; Offset into hash table
  1687.     mov     edi, edx        ; Save offset
  1688. ; Insert the entry at the head of the hash table
  1689. ; next[x] <- head
  1690.     mov     ax, [hHashTable]
  1691.     mov     [XMoveStruc.SourceHandle], ax
  1692.     mov     [XMoveStruc.SourceOffset], edx
  1693.     mov     [XMoveStruc.DestHandle], 0
  1694.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+06h
  1695.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  1696.     mov     ah, 0Bh
  1697.     call    [XMSEntry]
  1698.     or      ax, ax
  1699.     jz      @@XMSError
  1700. ; if head <> NIL then prev[head] <- x
  1701.     mov     ax, [WORD EntryBuf+06h]
  1702.     or      ax, ax
  1703.     jz      @@HashInsert2
  1704.     mov     dx, CacheEntrySize
  1705.     mul     dx
  1706.     add     ax, 04h         ; Prev
  1707.     adc     dx, 0
  1708.     mov     [WORD XMoveStruc.DestOffset], ax
  1709.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1710.     mov     ax, [hCacheLists]
  1711.     mov     [XMoveStruc.DestHandle], ax
  1712.     mov     [XMoveStruc.SourceHandle], 0
  1713.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
  1714.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1715.     mov     ah, 0Bh
  1716.     call    [XMSEntry]
  1717.     or      ax, ax
  1718.     jz      @@XMSError
  1719. @@HashInsert2:
  1720. ; head <- x
  1721.     mov     [XMoveStruc.SourceHandle], 0
  1722.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
  1723.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1724.     mov     ax, [hHashTable]
  1725.     mov     [XMoveStruc.DestHandle], ax
  1726.     mov     [XMoveStruc.DestOffset], edi    ; Saved offset into hash table
  1727.     mov     ah, 0Bh
  1728.     call    [XMSEntry]
  1729.     or      ax, ax
  1730.     jz      @@XMSError
  1731. ; prev[x] <- NIL
  1732.     mov     [WORD EntryBuf+04h], 0
  1733. ; Write the entry
  1734.     mov     ax, [hCacheLists]
  1735.     mov     [XMoveStruc.DestHandle], ax
  1736.     mov    ax, [@@EntryNum]
  1737.     mov     dx, CacheEntrySize
  1738.     mul     dx
  1739.     mov     [WORD XMoveStruc.DestOffset], ax
  1740.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1741.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf
  1742.     mov     [XMoveStruc.Length], CacheEntrySize
  1743.     mov     ah, 0Bh
  1744.     call    [XMSEntry]
  1745.     or      ax, ax
  1746.     jz      @@XMSError
  1747.  
  1748. ; Copy the sector to the cache
  1749.     mov     [XMoveStruc.Length], 512
  1750.     mov     eax, [@@SectorAddr]
  1751.     mov     [XMoveStruc.SourceOffset], eax
  1752.     mov     [XMoveStruc.SourceHandle], 0
  1753.     mov     ax, [hCacheSectors]
  1754.     mov     [XMoveStruc.DestHandle], ax
  1755.     movzx   eax, [@@EntryNum]
  1756.     shl     eax, 9
  1757.     mov     [XMoveStruc.DestOffset], eax
  1758.     mov     si, OFFSET XMoveStruc
  1759.     mov     ah, 0Bh
  1760.     call    [XMSEntry]
  1761.     or      ax, ax
  1762.     clc
  1763.     jnz     @@Done
  1764.  
  1765. @@XMSError:
  1766.     mov     [XMSError], 1   ; Flag XMS error
  1767.     stc
  1768. @@Done: popad
  1769.     ret
  1770. ENDP    CacheInsert
  1771.  
  1772. ;---------------------------------------------------------------------
  1773. ; Searches for a sector in the cache. Sector number in ECX. Returns
  1774. ; cache entry in BX. If sector is not in cache, BX=0000.
  1775. PROC    SearchCache STDCALL
  1776.     DEFASSUME
  1777.     LOCAL    @@Sector:DWORD
  1778. ; Compute the hash function (sector number modulo slots in hash table)
  1779.     cmp    [XMSError], 0
  1780.     jnz    @@XMSError
  1781.     mov    [@@Sector], ecx
  1782.     mov     ax, cx
  1783.     mov    edx, ecx
  1784.     shr    edx, 16        ; Sector in DX:AX
  1785.     div     [HashSize]      ; Hash slot in DX
  1786. ; Extract the linked list pointer from the hash table
  1787.     mov     [XMoveStruc.Length], 2
  1788.     mov     ax, [hHashTable]
  1789.     mov     [XMoveStruc.SourceHandle], ax
  1790.     shl    edx, 1
  1791.     mov     [XMoveStruc.SourceOffset], edx
  1792.     mov     [XMoveStruc.DestHandle], 0
  1793.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf
  1794.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  1795.     mov     si, OFFSET XMoveStruc
  1796.     mov     ah, 0Bh
  1797.     call    [XMSEntry]
  1798.     or      ax, ax
  1799.     jz      @@XMSError
  1800.     mov     bx, [WORD EntryBuf]
  1801. ; Walk the linked list and search for the sector
  1802. ; Entry format: Offset  Size    Descr
  1803. ;               00h     DWORD   Sector number
  1804. ;               04h     WORD    Prev entry in hash chain
  1805. ;               06h     WORD    Next entry in hash chain
  1806. ;               08h     WORD    Prev entry in priority list
  1807. ;               0Ah     WORD    Next entry in priority list
  1808. ;        0Ch    WORD    ResData segment of owner drive
  1809.     mov     [XMoveStruc.Length], CacheEntrySize
  1810.     mov     ax, [hCacheLists]
  1811.     mov     [XMoveStruc.SourceHandle], ax
  1812. @@SearchList:
  1813.     or      bx, bx
  1814.     je      @@Done        ; Sector not in cache, exit.
  1815. ; Copy the entry into first bytes of buffer
  1816.     mov     ax, bx
  1817.     mov     dx, CacheEntrySize
  1818.     mul     dx        ; Offset of entry in DX:AX
  1819.     mov    [WORD XMoveStruc.SourceOffset], ax
  1820.     mov    [(WORD XMoveStruc.SourceOffset)+2], dx
  1821.     mov     si, OFFSET XMoveStruc
  1822.     mov     ah, 0Bh
  1823.     push    bx
  1824.     call    [XMSEntry]
  1825.     pop    bx
  1826.     or      ax, ax
  1827.     jz      @@XMSError
  1828. ; Examine the entry in buffer
  1829.     mov     eax, [@@Sector]
  1830.     cmp     [DWORD EntryBuf], eax
  1831.     jne    @@Search1
  1832.     mov    ax, gs
  1833.         cmp    [WORD EntryBuf+0Ch], ax    ; Check if it's the right drive.
  1834.     je      @@Done        ; Sector found, exit with entry in BX.
  1835. @@Search1:
  1836.     mov     bx, [WORD EntryBuf+06h]       ; Next entry in chain
  1837.     jmp     @@SearchList
  1838.  
  1839. @@XMSError:
  1840.     mov     [XMSError], 1   ; Flag XMS error
  1841.     xor    bx, bx
  1842. @@Done:    ret
  1843. ENDP    SearchCache
  1844.  
  1845. ;---------------------------------------------------------------------
  1846. ; Delete a cache entry. Entry number in BX. Entry must be in use.
  1847. ; Returns carry set on XMS error.
  1848. PROC    DeleteCacheEntry
  1849.         pushad
  1850. ; Read the entry into buffer
  1851.     mov     [XMoveStruc.Length], CacheEntrySize
  1852.     mov     ax, bx
  1853.     mov     dx, CacheEntrySize
  1854.     mul     dx
  1855.     mov     [WORD XMoveStruc.SourceOffset], ax
  1856.     mov     [WORD (XMoveStruc.SourceOffset)+2], dx
  1857.     mov     ah, 0Bh
  1858.     call    [XMSEntry]
  1859.     or      ax, ax
  1860.     jz      @@XMSError
  1861. ; Delete the entry from the hash chain
  1862. ; if prev[x] <> NIL        
  1863. ;   then next[prev[x]] <- next[x]
  1864. ;   else head <- next[x]
  1865. ; if next[x] <> NIL
  1866. ;   then prev[next[x]] <- prev[x]
  1867.     mov     ax, [WORD EntryBuf+04h] ; Prev field. 
  1868.     or      ax, ax                      ; See if entry is head
  1869.     je      @@NewHead
  1870.     mov     dx, CacheEntrySize
  1871.     mul     dx
  1872.     add     ax, 06h                     ; Next field
  1873.     adc     dx, 0
  1874.     mov     [WORD XMoveStruc.DestOffset], ax
  1875.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1876.     mov     ax, [hCacheLists]
  1877.     mov     [XMoveStruc.DestHandle], ax
  1878.     mov     [XMoveStruc.SourceHandle], 0
  1879.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+06h
  1880.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1881.     mov     [XMoveStruc.Length], 2
  1882.     mov     si, OFFSET XMoveStruc
  1883.     mov     ah, 0Bh
  1884.     call    [XMSEntry]
  1885.     or      ax, ax
  1886.     jz      @@XMSError
  1887.     jmp     @@UpdateNextChainEntry
  1888. @@NewHead:
  1889.     xor     edx, edx
  1890.     mov     ax, [WORD EntryBuf]
  1891.     mov     dx, [WORD EntryBuf+2]   ; Sector in DX:AX
  1892.     div     [HashSize]              ; Hash slot in DX
  1893.     mov     ax, [hHashTable]
  1894.     mov     [XMoveStruc.DestHandle], ax
  1895.     shl     edx, 1
  1896.     mov     [XMoveStruc.DestOffset], edx
  1897.     mov     [XMoveStruc.SourceHandle], 0
  1898.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+06h
  1899.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1900.     mov     [XMoveStruc.Length], 2
  1901.     mov     si, OFFSET XMoveStruc
  1902.     mov     ah, 0Bh
  1903.     call    [XMSEntry]
  1904.     or      ax, ax
  1905.     jz      @@XMSError
  1906. @@UpdateNextChainEntry:
  1907.     mov     ax, [WORD EntryBuf+06h]
  1908.     or      ax, ax
  1909.     je      @@DeleteEntry2
  1910.     mov     dx, CacheEntrySize
  1911.     mul     dx
  1912.     add     ax, 04h
  1913.     adc     dx, 0
  1914.     mov     [WORD XMoveStruc.DestOffset], ax
  1915.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1916.     mov     ax, [hCacheLists]
  1917.     mov     [XMoveStruc.DestHandle], ax
  1918.     mov     [XMoveStruc.SourceHandle], 0
  1919.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+04h
  1920.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1921.     mov     [XMoveStruc.Length], 2
  1922.     mov     ah, 0Bh
  1923.     call    [XMSEntry]
  1924.     or      ax, ax
  1925.     jz      @@XMSError
  1926. @@DeleteEntry2:
  1927. ; Delete the entry from the priority list.
  1928. ; next[prev[x]] <- next[x]
  1929. ; prev[next[x]] <- prev[x]
  1930.     mov     ax, [WORD EntryBuf+08h]         ; Prev
  1931.     mov     dx, CacheEntrySize
  1932.     mul     dx
  1933.     add     ax, 0Ah                         ; Next field
  1934.     adc     dx, 0
  1935.     mov     [WORD XMoveStruc.DestOffset], ax
  1936.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1937.     mov     ax, [hCacheLists]
  1938.     mov     [XMoveStruc.DestHandle], ax
  1939.     mov     [XMoveStruc.SourceHandle], 0
  1940.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+0Ah
  1941.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  1942.     mov     [XMoveStruc.Length], 2
  1943.     mov     ah, 0Bh
  1944.     call    [XMSEntry]
  1945.     or      ax, ax
  1946.     jz      @@XMSError
  1947.  
  1948.     mov     ax, [WORD EntryBuf+0Ah]         ; Next
  1949.     mov     dx, CacheEntrySize
  1950.     mul     dx
  1951.     add     ax, 08h                         ; Prev field
  1952.     adc     dx, 0
  1953.     mov     [WORD XMoveStruc.DestOffset], ax
  1954.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  1955.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+08h
  1956.     mov     ah, 0Bh
  1957.     call    [XMSEntry]
  1958.     or      ax, ax
  1959.     jz      @@XMSError
  1960.     clc
  1961.     jmp    @@Done
  1962. @@XMSError:
  1963.     stc
  1964. @@Done: popad
  1965.     ret
  1966. ENDP    DeleteCacheEntry
  1967.  
  1968. ;---------------------------------------------------------------------
  1969. ; Return a free cache entry. If cache is full, then oldest entry is
  1970. ; deleted. Free entry returned in BX. BX=0000 if an XMS error occurs.
  1971. PROC    GetFreeCacheEntry STDCALL
  1972.     LOCAL    Entry
  1973.         pushad
  1974.     mov     bx, [FreeEntry]
  1975.     or      bx, bx
  1976.     jz    @@GetOldestEntry
  1977.     mov    [Entry], bx
  1978.     dec     [FreeEntry]
  1979.     jmp    @@Done
  1980. ; Get the oldest entry.        
  1981. @@GetOldestEntry:
  1982.     mov     [XMoveStruc.Length], 2
  1983.     mov     ax, [hCacheLists]
  1984.     mov     [XMoveStruc.SourceHandle], ax
  1985.     mov     [XMoveStruc.SourceOffset], 08h  ; Tail of priority list
  1986.     mov     [XMoveStruc.DestHandle], 0
  1987.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf
  1988.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  1989.     mov     si, OFFSET XMoveStruc
  1990.     mov     ah, 0Bh
  1991.     call    [XMSEntry]
  1992.     or      ax, ax
  1993.     jz      @@XMSError
  1994.     mov     bx, [WORD EntryBuf]     ; Entry to be deleted
  1995.     mov     [Entry], bx
  1996.     call    DeleteCacheEntry
  1997.     jc    @@XMSError
  1998.     jmp    @@Done
  1999. @@XMSError:
  2000.     mov     [XMSError], 1   ; Flag XMS error
  2001.     mov    [Entry], 0
  2002. @@Done: popad
  2003.     mov    bx, [Entry]
  2004.     ret
  2005. ENDP    GetFreeCacheEntry
  2006.  
  2007. ;---------------------------------------------------------------------
  2008. ; Read a sector from the cache. Cache entry in BX. The entry is moved
  2009. ; to the head of the priority list. Sector is copied to ES:DI.
  2010. ; Returns AX=0 if XMS error.
  2011. PROC    ReadCacheSector STDCALL
  2012.     LOCAL    @@Entry
  2013.     mov    [@@Entry], bx
  2014.     mov     [XMoveStruc.Length], 512
  2015.     mov     ax, [hCacheSectors]
  2016.     mov     [XMoveStruc.SourceHandle], ax
  2017.     movzx   eax, bx
  2018.     shl     eax, 9                  ; Multiply by 512
  2019.     mov     [XMoveStruc.SourceOffset], eax
  2020.     mov     [XMoveStruc.DestHandle], 0
  2021.     mov    [WORD XMoveStruc.DestOffset], di
  2022.     mov    [(WORD XMoveStruc.DestOffset)+2], es
  2023.     mov     si, OFFSET XMoveStruc
  2024.     mov     ah, 0Bh
  2025.     call    [XMSEntry]
  2026.     or      ax, ax
  2027.     jz      @@XMSError
  2028. ; Remove the entry from the priority list.
  2029. ; Entry 0 in priority list is a sentinel.        
  2030. ; next[prev[x]] <- next[x]
  2031.     mov     [XMoveStruc.Length], 2
  2032.     mov     [XMoveStruc.SourceHandle], 0
  2033.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  2034.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+0Ah ; Next
  2035.     mov     ax, [hCacheLists]
  2036.     mov     [XMoveStruc.DestHandle], ax
  2037.     mov     ax, [WORD EntryBuf+08h] ; Prev
  2038.     mov     dx, CacheEntrySize
  2039.     mul     dx                      ; Offset
  2040.     add     ax, 0Ah                 ; Next field of previous entry
  2041.     adc     dx, 0
  2042.     mov     [WORD XMoveStruc.DestOffset], ax
  2043.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  2044.     mov     ah, 0Bh
  2045.     call    [XMSEntry]        
  2046.     or      ax, ax
  2047.     jz      @@XMSError
  2048. ; prev[next[x]] <- prev[x]
  2049.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+08h ; Prev
  2050.     mov     ax, [WORD EntryBuf+0Ah] ; Next
  2051.     mov     dx, CacheEntrySize
  2052.     mul     dx                      ; Offset
  2053.     add     ax, 08h                 ; Prev field
  2054.     adc     dx, 0
  2055.     mov     [WORD XMoveStruc.DestOffset], ax
  2056.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  2057.     mov     ah, 0Bh
  2058.     call    [XMSEntry]        
  2059.     or      ax, ax
  2060.     jz      @@XMSError
  2061. ; Insert the entry at the head of the priority list
  2062. ; next[x] <- next[sentinel]
  2063.     mov     ax, [hCacheLists]
  2064.     mov     [XMoveStruc.SourceHandle], ax
  2065.     mov     [XMoveStruc.SourceOffset], 0Ah  ; Next field of sentinel
  2066.  
  2067.     mov     ax, [@@Entry]                   ; Entry number
  2068.     mov     dx, CacheEntrySize
  2069.     mul     dx
  2070.     mov     di, dx
  2071.     shl     edi, 16
  2072.     mov     di, ax
  2073.     add     edi, 0Ah                        ; Next field
  2074.     mov     [XMoveStruc.DestOffset], edi
  2075.     mov     ah, 0Bh
  2076.     call    [XMSEntry]
  2077.     or      ax, ax
  2078.     jz      @@XMSError
  2079. ; prev[next[sentinel]] <- x
  2080.     mov     [XMoveStruc.DestHandle], 0        
  2081.     mov     [WORD XMoveStruc.DestOffset], OFFSET EntryBuf+CacheEntrySize
  2082.     mov     [WORD (XMoveStruc.DestOffset)+2], ds
  2083.     mov     ah, 0Bh
  2084.     call    [XMSEntry]
  2085.     or      ax, ax
  2086.     jz      @@XMSError
  2087.     mov     ax, [WORD EntryBuf+CacheEntrySize]     ; next[sentinel]
  2088.     mov     dx, CacheEntrySize
  2089.     mul     dx
  2090.     add     ax, 08h                 ; prev field
  2091.     adc     dx, 0
  2092.     mov     [WORD XMoveStruc.DestOffset], ax
  2093.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  2094.     mov     ax, [hCacheLists]
  2095.     mov     [XMoveStruc.DestHandle], ax
  2096.     mov    ax, [@@Entry]
  2097.     mov     [WORD EntryBuf+CacheEntrySize], ax     ; Current entry
  2098.     mov     [XMoveStruc.SourceHandle], 0
  2099.     mov     [WORD XMoveStruc.SourceOffset], OFFSET EntryBuf+CacheEntrySize
  2100.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  2101.     mov     ah, 0Bh
  2102.     call    [XMSEntry]
  2103.     or      ax, ax
  2104.     jz      @@XMSError
  2105. ; next[sentinel] <- x
  2106.     mov     [XMoveStruc.DestOffset], 0Ah
  2107.     mov     ah, 0Bh
  2108.     call    [XMSEntry]
  2109.     or      ax, ax
  2110.     jz      @@XMSError
  2111. ; prev[x] <- sentinel
  2112.     mov     [WORD EntryBuf+CacheEntrySize], 0
  2113.     mov    ax, [@@Entry]
  2114.     mov     dx, CacheEntrySize
  2115.     mul     dx
  2116.     add     ax, 08h                 ; prev field
  2117.     adc     dx, 0
  2118.     mov     [WORD XMoveStruc.DestOffset], ax
  2119.     mov     [WORD (XMoveStruc.DestOffset)+2], dx
  2120.     mov     ah, 0Bh
  2121.     call    [XMSEntry]
  2122.     or      ax, ax
  2123.     jz      @@XMSError
  2124.     jmp    @@Done
  2125.  
  2126. @@XMSError:
  2127.     mov     [XMSError], 1   ; Flag XMS error
  2128. @@Done:    ret
  2129. ENDP    ReadCacheSector
  2130.  
  2131. ;---------------------------------------------------------------------
  2132. ; Read specified file sectors. Arguments:
  2133. ; @@Sector    - starting file sector
  2134. ; @@NumSectors    - number of sectors to read
  2135. ; @@FNode       - FNode sector number of file
  2136. ; @@Dest    - destination buffer
  2137. ; Uses Buf4. Return CF on error.
  2138. PROC    ReadFileSector PASCAL
  2139.     DEFASSUME
  2140.         ARG     @@Sector:DWORD, @@NumSectors, @@FNode:DWORD, @@Dest:DWORD
  2141.         LOCAL   @@Entries:WORD
  2142.         pushad
  2143. ; Read the FNode
  2144.         call    NEAR ReadSector, [@@FNode], 1, ds (OFFSET Buf4)
  2145.         mov     bx, OFFSET Buf4+38h
  2146. @@Start:
  2147.     mov    eax, [@@Sector]
  2148.     movzx    cx, [BYTE bx+05h]    ; number of entries in sector
  2149.     mov    [@@Entries], cx
  2150.     test    [BYTE bx], 80h        ; Internal or external node?
  2151.     jnz    @@Internal
  2152. ; Search external node - extents.
  2153.     sub    bx, 4            ; Point to 12 bytes before first entry
  2154.     xor    dx, dx            ; Entry counter
  2155. @@NextExtent:    
  2156.     add    bx, 12            ; Point to next entry
  2157.     inc    dx
  2158.     cmp    dx, [@@Entries]        ; Any entries left?
  2159.     ja    @@Error
  2160.     mov    ecx, [DWORD bx]        ; First file sector in this extent
  2161.     cmp    ecx, eax
  2162.     ja    @@Error            ; Too high - something's wrong
  2163.     add    ecx, [DWORD bx+04h]    ; Add number of sectors in extent
  2164.     sub    ecx, eax        ; Sector in this extent?
  2165.     jna    @@NextExtent
  2166. ; Extent found - calculate disk sector and read.
  2167.     mov    esi, ecx        ; Number of sectors left in extent
  2168.     mov    ecx, eax
  2169.     sub    ecx, [DWORD bx]        ; Offset into extent
  2170.     add    ecx, [DWORD bx+08h]    ; First logical sector number
  2171. ; Calculate number of sectors to read.
  2172.     movzx    edi, [@@NumSectors]
  2173.     cmp    esi, edi
  2174.     jbe    @@1
  2175.     mov    si, di
  2176. @@1:
  2177.     call    NEAR ReadSector, ecx, si, [@@Dest]
  2178.     jc    @@Error
  2179.     sub    [@@NumSectors], si
  2180.     clc
  2181.     jz    @@Done
  2182.     and    esi, 0FFFFh
  2183.     add    [@@Sector], esi
  2184. ; Reread FNode
  2185.         call    NEAR ReadSector, [@@FNode], 1, ds (OFFSET Buf4)
  2186.         mov     bx, OFFSET Buf4+38h
  2187.     mov    ax, 200h
  2188.     mul    si
  2189.     add    [WORD LOW @@Dest], ax    ; High word in DX not used, should be 0.
  2190.     jmp    @@Start
  2191.  
  2192. ; Search internal node
  2193. @@Internal:
  2194.     xor    dx, dx            ; Entry counter
  2195. @@NextSubtree:    
  2196.     add    bx, 8            ; Point to next entry
  2197.     inc    dx
  2198.     cmp    dx, [@@Entries]        ; Any entries left?
  2199.     ja    @@Error
  2200.     cmp    eax, [DWORD bx]    ; If below, then sector is in this tree.
  2201.     jae    @@NextSubtree
  2202. ; Subtree found - read sector and recurse.
  2203.     call    NEAR ReadSector, [DWORD bx+04h], 1, ds (OFFSET Buf4)
  2204.     jc    @@Error
  2205.     mov    bx, OFFSET Buf4+0Ch    ; Point to allocation structure
  2206.     jmp    @@Start
  2207.  
  2208. @@Error:
  2209.     stc
  2210. @@Done: popad
  2211.     ret
  2212. ENDP    ReadFileSector
  2213.  
  2214. ;---------------------------------------------------------------------
  2215. ; Convert a character in AL to upper case.
  2216. PROC    UpCase
  2217.     DEFASSUME
  2218.     cmp     al, 128
  2219.     jae     @@Extended
  2220.     cmp     al, 'a'
  2221.     jb      @@Done
  2222.     cmp     al, 'z'
  2223.     ja      @@Done
  2224.     sub     al, 'a'-'A'
  2225. @@Done: ret
  2226. @@Extended:
  2227.     push    bx ds
  2228.     push    cs
  2229.     pop     ds
  2230.     mov     bx, OFFSET UpCaseTbl-128
  2231.     xlatb
  2232.     pop     ds bx
  2233.     ret
  2234. ENDP    UpCase
  2235.  
  2236. ;---------------------------------------------------------------------
  2237. ; Checks if the character in AL is a valid filename character.
  2238. ; CF=1 if invalid. Registers preserved.
  2239. PROC    FileChar
  2240.     DEFASSUME
  2241.     push    ax ds
  2242.     push    cs
  2243.     pop     ds
  2244.     cmp     al, [MinPerm]
  2245.     jb      @@Fail
  2246.     cmp     al, [MaxPerm]
  2247.     ja      @@Fail
  2248.     cmp     al, [MinExcl]
  2249.     jb      @@1
  2250.     cmp     al, [MaxExcl]
  2251.     jna     @@Fail
  2252. @@1:
  2253.     push    cx di es
  2254.     push    ds
  2255.     pop     es
  2256.     ASSUME    es:ResCode
  2257.     mov     cl, [NumTerm]
  2258.     xor     ch, ch
  2259.     mov     di, OFFSET TermChars
  2260.     cld
  2261.     repne scasb
  2262.     pop     es di cx
  2263.     je      @@Fail
  2264.     clc     
  2265.     jmp     @@Done
  2266. @@Fail:
  2267.     stc
  2268. @@Done:
  2269.     pop     ds ax
  2270.     ret
  2271. ENDP    FileChar
  2272.  
  2273. ;---------------------------------------------------------------------
  2274. ; Checks if a filename matches a spec.
  2275. ; Filename at DS:DX. Filespec at ES:BX. 
  2276. ; Returns CF=0 if match.
  2277. PROC    Match
  2278.     DEFASSUME
  2279.     ASSUME    ds:NOTHING
  2280.     push    bx cx dx si di
  2281.     cld
  2282.     mov     si, dx
  2283. @@Next:
  2284.     mov     ah, [es:bx]
  2285.     inc     bx
  2286.     lodsb
  2287. ; Check if the character is valid in filenames
  2288.     or      al, al
  2289.     je      @@Valid
  2290.     cmp     al, '.'
  2291.     je      @@Valid
  2292.     call    FileChar
  2293.     jc      @@Fail
  2294. @@Valid:
  2295.     or      ah, ah          ; End of filespec?
  2296.     jne     @@1
  2297.     or      al, al
  2298.     jne     @@Fail
  2299.     jmp     @@Succeed
  2300. @@1:    cmp     ah, '?'
  2301.     jne     @@3
  2302.     cmp     al, '.'
  2303.     je      @@2
  2304.     or      al, al
  2305.     je      @@2
  2306.     jmp     @@Next
  2307. @@2:    dec     si
  2308.     jmp     @@Next
  2309. @@3:    cmp     ah, '.'
  2310.     jne     @@4
  2311.     or      al, al
  2312.     jne     @@4
  2313.     dec     si
  2314.     jmp     @@Next
  2315. @@4:    cmp     ah, al
  2316.     je      @@Next
  2317. @@Fail:
  2318.     stc
  2319.     jmp     @@Done
  2320. @@Succeed:
  2321.     clc
  2322. @@Done: pop     di si dx cx bx
  2323.     ret
  2324. ENDP    Match
  2325.  
  2326. ;---------------------------------------------------------------------
  2327. ; Check if filespec at DS:SI matches . file. Returns CF=0 if match.
  2328. ; Filespec matched: At least one <?>, optional <dot>, optional more <?>.
  2329. PROC    MatchDot
  2330.     NOASSUME
  2331.     ASSUME    cs:ResCode
  2332.         push    ax si
  2333. ; Match one <?>
  2334.     lodsb
  2335.     cmp    al, '?'
  2336.     jne    @@Fail
  2337. ; Eat all consecutive <?>
  2338. @@1:    lodsb
  2339.     cmp    al, '?'
  2340.     je    @@1
  2341.     or    al, al        ; End of filespec?
  2342.     je    @@Ok
  2343.     cmp    al, '.'        ; Match dot (separator)
  2344.     jne    @@Fail
  2345. ; Eat all consecutive <?>
  2346. @@2:    lodsb
  2347.     cmp    al, '?'
  2348.     je    @@2
  2349.     or    al, al        ; End of filespec?
  2350.     jne    @@Fail
  2351.  
  2352. @@Ok:    clc
  2353.     jmp    @@Done
  2354. @@Fail:    stc
  2355. @@Done:    pop    si ax
  2356.     ret
  2357. ENDP    MatchDot
  2358.     
  2359. ;---------------------------------------------------------------------
  2360. ; Check if filespec at DS:SI matches .. file. Returns CF=0 if match.
  2361. ; Filespec matched: At least two <?>, optional <dot>, optional more <?>.
  2362. PROC    MatchDotDot
  2363.     NOASSUME
  2364.     ASSUME    cs:ResCode
  2365.         push    ax si
  2366. ; Match two <?>
  2367.     lodsw
  2368.     cmp    ax, '??'
  2369.     jne    @@Fail
  2370. ; Eat all consecutive <?>
  2371. @@1:    lodsb
  2372.     cmp    al, '?'
  2373.     je    @@1
  2374.     or    al, al        ; End of filespec?
  2375.     je    @@Ok
  2376.     cmp    al, '.'        ; Match dot (separator)
  2377.     jne    @@Fail
  2378. ; Eat all consecutive <?>
  2379. @@2:    lodsb
  2380.     cmp    al, '?'
  2381.     je    @@2
  2382.     or    al, al        ; End of filespec?
  2383.     jne    @@Fail
  2384.  
  2385. @@Ok:    clc
  2386.     jmp    @@Done
  2387. @@Fail:    stc
  2388. @@Done:    pop    si ax
  2389.     ret
  2390. ENDP    MatchDotDot
  2391.     
  2392. ;---------------------------------------------------------------------
  2393. ; Convert an HPFS filename to a valid DOS filename. Only to be called
  2394. ; from FindNext.
  2395. ; DS:SI - filename to convert. ES:DI - buffer to place converted name.
  2396. ; CX - length of filename.
  2397. PROC    ConvertFilename STDCALL
  2398.     DEFASSUME
  2399.     LOCAL    @@Len:WORD
  2400.     pusha
  2401.     cld
  2402.     mov    [@@Len], cx
  2403.     xor    ah, ah
  2404. @@ConvName:
  2405.     jcxz    @@ConvExt
  2406.     lodsb
  2407.     dec    cx
  2408.     cmp    al, '.'
  2409.     je    @@ConvExt
  2410.     call    FileChar    ; Character valid in filenames?    
  2411.     jc    @@ConvName
  2412.     call    UpCase
  2413.     stosb
  2414.     inc    ah
  2415.     cmp    ah, 8
  2416.     jb    @@ConvName
  2417. @@ConvExt:
  2418.     or    ah, ah        ; Empty name?
  2419.     jnz    @@Conv1
  2420.     mov    eax, 'SFPH'    ; Dummy name
  2421.     stosd
  2422. @@Conv1:
  2423.     mov    al, '.'
  2424.     stosb
  2425.     lea    si, [Buf1+bx+1Fh]    ; Point to filename
  2426.     mov    cx, [@@Len]        ; Length of filename
  2427.     call    CRC16            ; CRC16 in DX
  2428.     mov    ax, dx
  2429.     xor    dx, dx
  2430. ; Convert to base-41 
  2431.     mov    bx, OFFSET ConvTable    ; Translation table
  2432.     mov    cx, 3
  2433. @@ConvBase:
  2434.     mov    si, 41
  2435.     div    si            ; Remainder in DX (DL)
  2436.     xchg    al, dl
  2437.     xlatb
  2438.     stosb
  2439.     mov    al, dl
  2440.     xor    dx, dx
  2441.     loop    @@ConvBase
  2442.     xor    al, al            ; Zero terminate
  2443.     stosb
  2444.     popa
  2445.     ret
  2446. ENDP    ConvertFilename
  2447.  
  2448. ;---------------------------------------------------------------------
  2449. ; Find a directory entry. FNode sector of directory passed in ECX. 
  2450. ; Filename at DS:DX. Assumes DS=CS.
  2451. ; CF=1 if failed.
  2452. ; The sector is read into Buf1, and BX contains the offset to the
  2453. ; specified entry on return. Uses last half of FNameBuf
  2454. PROC    FindDirEntry STDCALL
  2455.     DEFASSUME
  2456.     ASSUME    es:ResCode
  2457.     LOCAL   @@Len:BYTE, @@BytesRead:WORD, @@Sector:DWORD
  2458.     push    es
  2459.     push    cs
  2460.     pop    es
  2461.     mov    [@@Sector], ecx
  2462. ; Count characters in filename.
  2463.     mov     di, dx
  2464.     xor     al, al
  2465.     mov     cx, 0FFh
  2466.     cld
  2467.     repne scasb
  2468.     neg     cl
  2469.     sub     cl, 2
  2470.     or      cl, cl
  2471.     jz      @@Fail
  2472.     mov     [@@Len], cl
  2473.     mov     [@@BytesRead], 200h       ; Bytes of directory block in memory
  2474. ; Read the FNode of the directory
  2475.     call    NEAR ReadSector, [@@Sector], 1, es (OFFSET Buf1)
  2476.     jc    @@Fail
  2477.     mov     ecx, [DWORD Buf1+48h]
  2478.     mov     [@@Sector], ecx
  2479.     call    NEAR ReadSector, ecx, 4, es (OFFSET Buf1)
  2480.     jc    @@Fail
  2481.  
  2482.     mov     bx, 14h         ; Offset of first dir. entry
  2483.     cld
  2484.     cmp    [ConvertLong], 0
  2485.     je    @@TestEntry
  2486. ; Traverse the directory B Tree to find a match. Convert long filenames.
  2487. ; All entries must be searched.
  2488. @@ChkValidName:
  2489.     mov    di, OFFSET FNameBuf+64    ; Load regs for call or move later
  2490.     lea    si, [Buf1+bx+1Fh]
  2491.     movzx    cx, [BYTE si-1]
  2492.     test    [Buf1+bx+03h], 40h
  2493.     jz    @@DOSName
  2494. ; Convert filename
  2495.     call    ConvertFilename
  2496.     jmp    @@CompareName
  2497. ; Copy filename to buffer
  2498. @@DosName:
  2499.     test    [Buf1+bx+02h], 01h    ; Skip '.' file
  2500.     jnz    @@NextLong
  2501.     push    cx di
  2502. @@CopyName:
  2503.     lodsb
  2504.     call    UpCase
  2505.     stosb
  2506.     loop    @@CopyName
  2507.     xor    al, al
  2508.     stosb
  2509.     pop    di cx
  2510. @@CompareName:
  2511.     mov    si, dx
  2512. @@Cmp1:
  2513.     lodsb
  2514.     or    al, al
  2515.     jz    @@EndOfTmpl        ; End of search filename
  2516.     scasb
  2517.     je    @@Cmp1
  2518.     jmp    @@NextLong
  2519. @@EndOfTmpl:
  2520.     cmp    [BYTE es:di], 0        ; Check that filename in entry also ends
  2521.     je    @@Succeed
  2522. @@NextLong:
  2523. ; See if there's a B Tree pointer
  2524.     test    [BYTE Buf1+bx+02h], 04h
  2525.     jz      @@NoBTree
  2526. ; Go down the tree
  2527.     add     bx, [WORD Buf1+bx]
  2528.     mov     eax, [DWORD Buf1+bx-04h]    ; B Tree pointer
  2529.     mov    [@@Sector], eax
  2530.     call    NEAR ReadSector, eax, 4, es (OFFSET Buf1)
  2531.     jc    @@Fail
  2532.     mov    bx, 14h
  2533.     jmp    @@ChkValidName            ; Search this block
  2534. @@NoBTree:
  2535. ; Move to next entry
  2536. @@NextEntry:
  2537.     test    [BYTE Buf1+bx+02h], 08h     ; See if last entry
  2538.     jnz     @@Up
  2539.     add     bx, [WORD Buf1+bx]          ; Point to next entry
  2540.     jmp    @@ChkValidName            ; Continue search    
  2541. ; Go up the tree
  2542. @@Up:
  2543.     mov    eax, [DWORD Buf1+0Ch]    ; Parent node
  2544.     call    NEAR ReadSector, eax, 4, es (OFFSET Buf1)
  2545.     jc    @@Fail
  2546.     cmp     [BYTE Buf1+03h], 0F7h      ; FNode signature
  2547.     je    @@Fail
  2548. ; Go through this directory block to find the entry that we came from.
  2549.     mov     bx, 14h
  2550. @@SrchParent:
  2551.     test    [Buf1+bx+02h], 04h      ; Entry has B tree pointer?
  2552.     jz      @@NotParent
  2553.     mov     si, bx
  2554.     add     si, [WORD Buf1+bx]
  2555.     mov    ecx, [@@Sector]
  2556.     cmp     [DWORD Buf1+si-4], ecx    ; Is parent?
  2557.     jne     @@NotParent
  2558.     mov    [@@Sector], eax
  2559.     jmp    @@NextEntry
  2560. @@NotParent:    
  2561.     test    [Buf1+bx+02h], 08h      ; Last entry in block?
  2562.     jnz     @@Fail
  2563.     add     bx, [WORD Buf1+bx]
  2564.     jmp     @@SrchParent
  2565.     
  2566. ; Traverse the directory B Tree to find a match. No long filenames.
  2567. @@TestEntry:
  2568.     cld
  2569. ; Compare the filename length byte
  2570.     mov     cl, [@@Len]
  2571.     cmp     cl, [Buf1+bx+1Eh]
  2572.     jna     @@1
  2573.     mov     cl, [Buf1+bx+1Eh]       ; Entry shorter than filename
  2574. ; Compare the filenames
  2575. @@1:
  2576.     xor     ch, ch
  2577.     mov     di, dx
  2578.     lea     si, [Buf1+bx+1Fh]
  2579. @@Compare:
  2580.     lodsb                   ; Load one character
  2581.     call    UpCase          ; Convert to upper case
  2582.     scasb                   ; Compare
  2583.     loope   @@Compare
  2584.     jb      @@Next
  2585.     ja      @@ChkBTree
  2586.     mov     cl, [@@Len]       ; See which of the names is the longest
  2587.     cmp     cl, [Buf1+bx+1Eh]
  2588.     je      @@Succeed
  2589.     ja      @@Next
  2590. ; See if there's a B Tree pointer
  2591. @@ChkBTree:
  2592.     test    [BYTE Buf1+bx+02h], 04h
  2593.     jz      @@Fail
  2594.     add     bx, [WORD Buf1+bx]
  2595.     mov     ecx, [DWORD Buf1+bx-4]      ; Extract the B Tree pointer
  2596.     mov    [@@Sector], ecx
  2597.     mov     [@@BytesRead], 200h
  2598.     call    NEAR ReadSector, [@@Sector], 1, es (OFFSET Buf1)
  2599.     mov     bx, 14h
  2600.     jmp     @@TestEntry
  2601.  
  2602. @@Next:
  2603.     test    [BYTE Buf1+bx+02h], 08h     ; See if last entry
  2604.     jnz     @@Fail
  2605.     add     bx, [WORD Buf1+bx]          ; Point to next entry
  2606.     mov     ax, [WORD Buf1+bx]
  2607.     add     ax, bx
  2608.     cmp     ax, [@@BytesRead]                 ; See if enough sectors read.
  2609.     jb      @@TestEntry
  2610.     cmp     [@@BytesRead], 800h
  2611.     jnb     @@Fail                          ; Whole dir. block already read.
  2612.     inc     [@@Sector]
  2613.     push    bx
  2614.     mov     bx, [@@BytesRead]
  2615.     add     bx, OFFSET Buf1
  2616.     call    NEAR ReadSector, [@@Sector], 1, es bx
  2617.     pop     bx
  2618.     add     [@@BytesRead], 200h
  2619.     jmp     @@TestEntry     
  2620.  
  2621. @@Fail:
  2622.     stc
  2623.     jmp     @@Done
  2624. @@Succeed:
  2625.     clc
  2626. @@Done: 
  2627.     pop     es
  2628.     ret
  2629. ENDP    FindDirEntry
  2630.  
  2631. ;---------------------------------------------------------------------
  2632. ; Find a file. Fully qualified filename at ES:DI. Offset into
  2633. ; Buf1 to directory entry returned in BX.
  2634.  
  2635. PROC    FindFile STDCALL
  2636.     DEFASSUME
  2637.     LOCAL   @@DirFlag:BYTE, @@CurFNode:DWORD, @@RetValue, @@LongPath:BYTE
  2638.     LOCAL    @@BufStart
  2639.     pushad
  2640.     push    ds
  2641.     push    cs
  2642.     pop     ds
  2643.     mov     [@@DirFlag], 0            ; Set if current level is also the last.
  2644.     mov    [@@LongPath], 0        ; Set if path is too long for any more subdirs.
  2645.     mov    [@@BufStart], di
  2646.     mov     ecx, [RootFNode]
  2647.     mov     [@@CurFNode], ecx
  2648. ; Scan past the first backslash.
  2649.     mov     al, '\'
  2650.     mov     cx, 80h
  2651.     cld
  2652.     repne scasb
  2653.     dec     di
  2654. @@NextLevel:
  2655.     cmp    [@@LongPath], 0
  2656.     jne    @@Fail            ; Path too long - can't access directory.
  2657. ; See if path is too long
  2658.     mov    ax, di
  2659.     sub    ax, [@@BufStart]
  2660.     cmp    ax, MaxPathLength-9
  2661.     seta    [@@LongPath]
  2662.  
  2663.     mov     bx, OFFSET FNameBuf
  2664.     mov     dx, bx
  2665. ; Move the name of the next subdirectory to FNameBuf
  2666. @@MoveName:
  2667.     inc     di
  2668.     mov     al, [es:di]
  2669.     or      al, al
  2670.     je      @@SetFlag
  2671.     cmp     al, '\'
  2672.     je      @@FindEntry
  2673.     mov     [bx], al
  2674.     inc     bx
  2675.     jmp     @@MoveName
  2676. @@SetFlag:
  2677.     mov     [@@DirFlag], 1            ; This is the last level
  2678. @@FindEntry:
  2679.     mov     [BYTE bx], 0
  2680. ; Find the directory entry
  2681.     mov     ecx, [@@CurFNode]
  2682.     push    di
  2683.     call    FindDirEntry
  2684.     mov     [@@RetValue], bx
  2685.     pop     di
  2686.     jc      @@Fail
  2687.     mov     ecx, [DWORD Buf1+bx+04h]    ; Get FNode pointer
  2688.     mov     [@@CurFNode], ecx
  2689.     cmp     [@@DirFlag], 0
  2690.     je      @@NextLevel
  2691.     clc
  2692.     jmp     @@Done
  2693.  
  2694. @@Fail: stc
  2695. @@Done:
  2696.     pop     ds
  2697.     popad
  2698.     jc      @@Exit
  2699.     mov     bx, [@@RetValue]
  2700. @@Exit:
  2701.     ret
  2702. ENDP    FindFile
  2703.  
  2704. ;---------------------------------------------------------------------
  2705. ; Check if a fully qualified pathname is too long to be the name of a
  2706. ; directory, and should be handled as a zero-length file. CF=1 if path
  2707. ; is too long.
  2708. PROC    ChkPathLength PASCAL
  2709.     DEFASSUME
  2710.     ARG    @@FileName:DWORD
  2711.     pushad
  2712.     push    es
  2713.     les    di, [@@FileName]
  2714.     xor    al, al
  2715.     mov    cx, 128
  2716.     cld
  2717.     repne scasb
  2718.     mov    al, '\'
  2719.     std
  2720.     mov    cx, 128
  2721.     repne scasb
  2722.     cld
  2723.     sub    di, [WORD LOW @@FileName]
  2724.     cmp    di, MaxPathLength-9
  2725.     cmc                ; CF indicates result
  2726.     pop    es
  2727.     popad
  2728.     ret
  2729. ENDP    ChkPathLength
  2730.  
  2731. ;---------------------------------------------------------------------
  2732. ; Convert UNIX (and HPFS) timestamp to DOS timestamp (local time).
  2733. ; UNIX timestamp passed in EAX and DOS timestamp returned in EAX -
  2734. ; time in low word, date in high word.
  2735. PROC    Unix2DosTime STDCALL
  2736.     DEFASSUME
  2737.     LOCAL   @@Result:DWORD,@@Year,@@Month,@@Day,@@Hour,@@Min,@@Secs
  2738.     pushad
  2739.     push    ds
  2740.     push    cs
  2741.     pop     ds
  2742.     sub     eax, 24*60*60*3652+TimeZone+60*60
  2743.     xor     edx, edx
  2744.     mov     ebx, 60
  2745.     div     ebx
  2746.     mov     [@@Secs], dx
  2747.  
  2748.     xor     edx, edx
  2749.     div     ebx
  2750.     mov     [@@Min], dx               ; Time in minutes in EAX
  2751.  
  2752.     mov     ebx, 1461*24
  2753.     xor     edx, edx
  2754.     div     ebx
  2755.     shl     eax, 2
  2756.     add     eax, 1980
  2757.     mov     [@@Year], ax
  2758.     
  2759.     mov     eax, edx
  2760.     cmp     eax, 366*24
  2761.     jb      @@1
  2762.     sub     eax, 366*24
  2763.     inc     [@@Year]
  2764.     mov     ebx, 365*24
  2765.     xor     edx, edx
  2766.     div     ebx
  2767.     add     [@@Year], ax
  2768.     mov     ax, dx
  2769.  
  2770. @@1:                                    ; Now in AX
  2771.     xor     dx, dx
  2772.     mov     bx, 24
  2773.     div     bx
  2774.     mov     [@@Hour], dx
  2775.     inc     ax
  2776.     
  2777.     test    [@@Year], 3
  2778.     jnz     @@2
  2779.     cmp     ax, 60
  2780.     jna     @@3
  2781.     dec     ax
  2782.     jmp     @@2
  2783. @@3:    jne     @@2
  2784.     mov     [@@Month], 2
  2785.     mov     [@@Day], 29
  2786.     jmp     @@DecodeDone
  2787.  
  2788. @@2:    
  2789.     xor     bx, bx
  2790. @@2_1:
  2791.     mov     dl, [DaysInMonth+bx]
  2792.     xor     dh, dh
  2793.     cmp     dx, ax
  2794.     jnb     @@4
  2795.     sub     ax, dx
  2796.     inc     bx
  2797.     jmp     @@2_1
  2798. @@4:    inc     bx
  2799.     mov     [@@Month], bx
  2800.     mov     [@@Day], ax
  2801.         
  2802. @@DecodeDone:
  2803. ; Pack the time in DOS format.
  2804.     xor     eax, eax
  2805.     mov     ax, [@@Day]
  2806.     mov     bx, [@@Month]
  2807.     shl     bx, 5
  2808.     or      ax, bx
  2809.     mov     bx, [@@Year]
  2810.     sub     bx, 1980
  2811.     shl     bx, 9
  2812.     or      ax, bx
  2813.     
  2814.     shl     eax, 16
  2815.     mov     ax, [@@Secs]
  2816.     shr     ax, 1
  2817.     mov     bx, [@@Min]
  2818.     shl     bx, 5
  2819.     or      ax, bx
  2820.     mov     bx, [@@Hour]
  2821.     shl     bx, 11
  2822.     or      ax, bx
  2823.     mov     [@@Result], eax
  2824.  
  2825.     pop     ds
  2826.     popad
  2827.     mov     eax, [@@Result]
  2828.     ret
  2829. ENDP    Unix2DosTime
  2830.  
  2831. ;---------------------------------------------------------------------
  2832. ; Compute CRC16 of a string. DS:SI points at the buffer, CX is the
  2833. ; length of the buffer. CRC16 returned in DX.
  2834.  
  2835. PROC    CRC16
  2836.     NOASSUME
  2837.     ASSUME    cs:ResCode
  2838.         push    ax bx
  2839.     pushf
  2840.     cld
  2841.     xor    dx, dx
  2842. @@1:    lodsb
  2843.     xor    ah, ah
  2844.     xchg    ah, al
  2845.     xor    dx, ax
  2846.     push    cx
  2847.     mov    cx, 8
  2848. @@2:    mov    bx, dx
  2849.     shl    dx, 1
  2850.     and    bx, 8000h
  2851.     jz    @@3
  2852.     xor    dx, 1021h
  2853. @@3:    loop    @@2
  2854.     pop    cx
  2855.     loop    @@1
  2856.     popf
  2857.     pop    bx ax
  2858.     ret
  2859. ENDP    CRC16
  2860.  
  2861. PROC    ThunkReadSector PASCAL FAR
  2862.     ARG    @@Sector:DWORD,@@NumSectors:WORD,@@Dest:DWORD
  2863.     call    NEAR ReadSector, [@@Sector], [@@NumSectors], [@@Dest]
  2864.     ret
  2865. ENDP    ThunkReadSector
  2866.  
  2867.     DB      64 DUP ('RESSTACK')
  2868. LABEL   ResStack
  2869. ENDS    ResCode
  2870.  
  2871. ;================================================== Resident data
  2872. SEGMENT    ResData
  2873. PartitionNr DB    0        ; HPFS partition number
  2874. ; Partition info
  2875. nSecs    DB      0        ; Sectors per track
  2876. nHeads    DW      0        ; Heads
  2877. LBAstart DD    0        ; LBA starting sector
  2878. AccessMethod DB 0        ; Disk access method
  2879. PhysDrv DB      80h
  2880. ; File system info
  2881. RootFNode DD    0               ; FNode sector for root dir.
  2882. CDFNode DD      0               ; FNode sector for current dir.
  2883. TotalSectors DD    0        ; Partition size in sectors
  2884. FreeSectors DD  0               ; # of sectors marked as free in bitmaps
  2885. MediaID    DB    0        ; Media ID byte from boot block
  2886. Volabel DB      12 DUP (0)      ; Volume label + an extra null.
  2887. LABEL    EndResdata
  2888. ENDS    ResData
  2889.  
  2890. ;======================================================= Transient section
  2891.     NOASSUME
  2892. SEGMENT CSeg
  2893.     ASSUME  cs:CSeg,ds:DSeg,es:NOTHING,ss:SSeg,fs:ResCode,gs:ResCode
  2894. PROC    Main
  2895.     mov     ax, DSeg
  2896.     mov     ds, ax
  2897.     mov     es, ax
  2898.     mov     ax, ResCode
  2899.     mov    fs, ax
  2900.     mov     gs, ax
  2901.  
  2902. ; Write hello message
  2903.     mov     ah, 09h
  2904.     mov     dx, OFFSET MsgHello
  2905.     int     21h
  2906.  
  2907. ; Check for DR-DOS or Novell DOS
  2908.     mov    ax, 4452h
  2909.     stc
  2910.     int    21h
  2911.     jc    @@NotDrDos
  2912. ; DR-DOS or Novell DOS found. Check version.
  2913.     cmp    ah, 10h
  2914.     jne    errBadDosVer
  2915.     cmp    al, 72h        ; Novell DOS
  2916.     je    @@NovellDOS
  2917.     mov    [DrDos], al    ; DR-DOS version code
  2918.     cmp    al, 65h        ; DR-DOS 5
  2919.     je    @@Dos3
  2920.     cmp    al, 67h        ; DR-DOS 6
  2921.     je    @@Dos3
  2922.     jmp    errBadDosVer
  2923. @@NovellDOS:
  2924.     mov    [Novell], 1
  2925.     jmp    @@DosVerOk
  2926. @@NotDrDos:
  2927. ; Check DOS version
  2928.     mov     ax, 3000h
  2929.     int     21h
  2930.     cmp    al, 3
  2931.     je    @@Major3
  2932.     jb      errBadDOSVer
  2933.     cmp     al, 10
  2934.     jae    errBadDOSVer
  2935.     jmp    @@DosVerOk
  2936. ; DOS 3.x, check minor version is at least 10
  2937. @@Major3:
  2938.     cmp    ah, 10
  2939.     jb    errBadDOSVer
  2940. ; DOS 3.10-3.x or DR DOS 5 and 6
  2941. @@Dos3:
  2942.     mov    [CDSSize], 51h
  2943.     mov    [WORD LOW pCurrCDS], 26Ch
  2944.     mov    [WORD LOW FN1], 92h
  2945.     mov    [WORD LOW AccMode], 23Bh
  2946.     mov    [WORD LOW SrchAttr], 23Ah
  2947.  
  2948. @@DosVerOk:
  2949.  
  2950. ; Get PSP
  2951.     mov     ah, 51h
  2952.     int     21h
  2953.     mov     [PSP], bx
  2954. ; Release heap space above end of stack
  2955.     mov    bx, sp
  2956.     add    bx, 15
  2957.     shr    bx, 4
  2958.     mov    ax, ss
  2959.     add    bx, ax
  2960.     mov    ax, [PSP]
  2961.     sub    bx, ax
  2962.     push    es
  2963.     mov    es, ax
  2964.     mov    ah, 4Ah
  2965.     int    21h
  2966.     pop    es
  2967.     jc    errMemRel
  2968. ; Get SDA address
  2969.     mov     ax, 5D06h
  2970.     int     21h             ; Address returned in DS:SI
  2971.     ASSUME  ds:NOTHING
  2972.     jc      errNoSDA
  2973.  
  2974.     mov     [WORD HIGH SDA], ds
  2975.     mov     [WORD LOW SDA], si
  2976.     mov    [WORD HIGH pCurrCDS], ds
  2977.     add    [WORD LOW pCurrCDS], si
  2978.     mov    [WORD HIGH pDTA], ds
  2979.     add    [WORD LOW pDTA], si
  2980.     mov    [WORD HIGH FN1], ds
  2981.     add    [WORD LOW FN1], si
  2982.     mov    [WORD HIGH AccMode], ds
  2983.     add    [WORD LOW AccMode], si
  2984.     mov    [WORD HIGH SrchAttr], ds
  2985.     add    [WORD LOW SrchAttr], si
  2986.     mov    [WORD HIGH ExtOpenMode], ds
  2987.     add    [WORD LOW ExtOpenMode], si
  2988.     mov     ax, DSeg
  2989.     mov     ds, ax
  2990.     ASSUME  ds:DSeg
  2991. ; Get address of LoL
  2992.     mov     ah, 52h
  2993.     int     21h             ; ES:BX -> List of Lists
  2994.     ASSUME  es:NOTHING
  2995.     mov     [WORD LOW LstOfLst], bx
  2996.     mov     [WORD HIGH LstOfLst], es
  2997. ; Get Lastdrive
  2998.     mov     cl, [es:bx+21h]
  2999.     mov     [LastDrive], cl
  3000. ; Check if XMS is present, and get driver entry point
  3001.     mov     ax, 4300h
  3002.     int     2Fh
  3003.     cmp     al, 80h
  3004.     jne     NoXMS
  3005.     mov     [XMSFound], 1
  3006.     mov     ax, 4310h
  3007.     int     2Fh
  3008.     mov     [WORD LOW XMSEntry], bx
  3009.     mov     [WORD HIGH XMSEntry], es
  3010. NoXMS:
  3011.  
  3012. ; Parse command line
  3013.     call    ParseCmdLine
  3014.     cmp     [UnInstall], 1
  3015.     jne     InstallDriver
  3016.  
  3017. ; Scan through INT 2D functions 00h-0FFh in descending order.
  3018.     mov    [ApiFunc], 0FFh
  3019. CallMultiplex:
  3020.     mov    ah, [ApiFunc]
  3021.     xor    al, al        ; Installation check
  3022.     int     2Dh
  3023.     cmp    al, 0FFh
  3024.     jne    @@NextMultiplex
  3025.     mov    bx, ResCode    ; Compare signature strings
  3026.     mov    si, OFFSET AMISSign
  3027.     push    ds
  3028.     mov    ds, bx
  3029.     mov    es, dx        ; ES=driver's ResCode segment
  3030.     mov    cx, 4
  3031.     cld
  3032.     repe cmpsd
  3033.     pop    ds
  3034.     jne    @@NextMultiplex
  3035. ; Installed iHPFS found.
  3036.     mov    [DriverLoaded], 1
  3037.         cmp    [SpecUninstall], 0
  3038.     jz    @@TotalUninstall    ; Uninstall all drives
  3039. ; Loop through list of drives to uninstall.
  3040.     mov    bx, -1
  3041. @@UninstallDrives:
  3042.     inc    bx
  3043.     cmp    bx, 26
  3044.     je    @@NextMultiplex
  3045.     cmp    [UninstallDrv+bx], 0
  3046.     je    @@UninstallDrives
  3047.     movzx    ax, [ApiFunc]
  3048.     call    NEAR QueryDrive, bx, ax
  3049.         or    ah, ah
  3050.     jz    @@UninstallDrives
  3051.     mov    [UninstallDrv+bx], 0    ; Driver found
  3052.     call    NEAR RemoveDrv, bx, es
  3053.         or    ah, ah
  3054.         jnz    @@errRmDrvFail
  3055.     mov    dl, bl
  3056.         add    dl, 'A'
  3057.         mov    [MsgDrvRemovedLetter], dl
  3058.         mov    ah, 9
  3059.         mov    dx, OFFSET MsgDrvRemoved
  3060.         int    21h
  3061.     jmp    @@UninstallDrives
  3062. @@errRmDrvFail:
  3063.     mov    dl, bl
  3064.         add    dl, 'A'
  3065.         mov    [MsgCantRmDrvLetter], dl
  3066.     mov    dx, OFFSET MsgCantRmDrv
  3067.     mov    ah, 9
  3068.     int    21h
  3069.     jmp    @@UninstallDrives
  3070.         
  3071. @@TotalUninstall:
  3072.     call    NEAR UninstallDriver, es
  3073.     mov    [Installed], 1        ; Driver uninstalled
  3074.         jmp    @@NextMultiplex
  3075.        
  3076. @@NextMultiplex:
  3077.     sub    [ApiFunc], 1
  3078.     jnc    CallMultiplex
  3079.     cmp    [DriverLoaded], 0
  3080.         je    @@errNotLoaded
  3081.     cmp    [SpecUninstall], 0
  3082.         jnz    @@ChkUninstallResult
  3083.     cmp    [Installed], 0
  3084.         je    @@errCantUninstall
  3085. ; Total uninstall done.
  3086.     mov     ah, 9
  3087.     mov     dx, OFFSET MsgUnInstalled
  3088.     int     21h
  3089.     mov     ax, 4C00h
  3090.     int     21h
  3091.  
  3092. ; Check if any drives couldn't be uninstalled.
  3093. @@ChkUninstallResult:
  3094.     mov    bx, -1
  3095. @@ChkUninstall1:
  3096.     inc    bx
  3097.     cmp    bx, 26
  3098.     je    @@Exit
  3099.     cmp    [UninstallDrv+bx], 0
  3100.     je    @@ChkUninstall1
  3101.     mov    dl, bl
  3102.         add    dl, 'A'
  3103.         mov    [MsgDrvNotInstLetter], dl
  3104.     mov    dx, OFFSET MsgDrvNotInst
  3105.     mov    ah, 9
  3106.     int    21h
  3107.     jmp    @@ChkUninstall1
  3108.          
  3109. InstallDriver:
  3110.     ASSUME  cs:CSeg,ds:DSeg,es:NOTHING,fs:NOTHING,ss:SSeg,gs:ResCode
  3111. ; Release environment block.
  3112.     mov     es, [PSP]
  3113.     mov     ax, [es:2Ch]
  3114.     mov     es, ax
  3115.     mov     ah, 49h
  3116.     int     21h
  3117.     jc      errEnvRel
  3118.  
  3119. ; Find an unused Multiplex function
  3120.     xor    ah, ah
  3121. FindFreeMux:
  3122.     xor     al, al                  ; Installation check
  3123.     push    ax
  3124.     int     2Dh
  3125.     or      al, al
  3126.     pop     ax
  3127.     je      FoundFreeMux
  3128.     add    ah, 1
  3129.     jz    errNoFreeMux
  3130.     jmp     FindFreeMux
  3131. FoundFreeMux:
  3132.     mov     [ApiFunc], ah          ; Save Multiplex function
  3133.  
  3134.     call    NEAR ScanDisks
  3135. ; Check if any specified partitions weren't found.
  3136.     mov    bx, -1
  3137. @@ChkPartFound:
  3138.     inc    bx
  3139.     cmp    bx, 26
  3140.     je    @@ChkPartFound1
  3141.     cmp    [Partitions+bx], 0FEh
  3142.     jae    @@ChkPartFound
  3143.     mov    ax, bx
  3144.     mov    dl, 10
  3145.     div    dl
  3146.     or    al, al
  3147.     jz    @@ChkPartFound2
  3148.     add    al, 30h
  3149. @@ChkPartFound2:
  3150.     add    ah, 30h
  3151.     mov    [WORD MsgPartNotFoundNr], ax
  3152.     mov    dx, OFFSET MsgPartNotFound
  3153.     mov    ah, 9
  3154.     int    21h
  3155.     mov    [ErrSignaled], 1
  3156.     jmp    @@ChkPartFound
  3157. @@ChkPartFound1:
  3158.     cmp    [Installed], 0
  3159.     jnz    @@Installed    ; One or more drives installed
  3160.     cmp    [ErrSignaled], 0
  3161.         jnz    @@Exit        ; Error message already displayed
  3162.     jmp    errNoHPFS    ; No HPFS partitions found
  3163.  
  3164. ; One or more drives successfully installed.
  3165. @@Installed:
  3166.     mov    ax, ResCode
  3167.         mov    es, ax
  3168.     ASSUME    es:ResCode
  3169. ; Get File Character Upcase Table
  3170.     mov     ax, 6504h
  3171.     mov     bx, 0FFFFh
  3172.     mov     cx, 5
  3173.     mov     dx, 0FFFFh
  3174.     mov     di, OFFSET TermChars
  3175.     int     21h
  3176.     jc      @@GetFileChar
  3177.     push    ds
  3178.     lds     si, [DWORD TermChars+1]
  3179.         add     si, 2
  3180.     mov     di, OFFSET UpCaseTbl
  3181.     mov     cx, 32
  3182.     cld
  3183.     rep movsd
  3184.     pop     ds
  3185.  
  3186. ; Get File-Character Table
  3187. @@GetFileChar:
  3188.     mov     ax, 6505h       ; Get File-Character Table
  3189.     mov     bx, 0FFFFh      ; Default codepage
  3190.     mov     cx, 5           ; Buffer size
  3191.     mov     dx, 0FFFFh      ; Default country ID
  3192.     mov     di, OFFSET TermChars
  3193.     int     21h
  3194.     jc      @@FileChar2
  3195.     mov     si, [WORD TermChars+1]
  3196.     mov     ax, [WORD TermChars+3]
  3197.     mov     ds, ax
  3198.     ASSUME    ds:NOTHING
  3199.         add     si, 3
  3200.     cld
  3201.     lodsw
  3202.     mov     [WORD MinPerm], ax
  3203.     inc     si
  3204.     lodsw
  3205.     mov     [WORD MinExcl], ax
  3206.     inc     si
  3207.     lodsb
  3208.     mov     cl, al
  3209.     mov     [NumTerm], al
  3210.     xor     ch, ch
  3211.     mov     di, OFFSET TermChars
  3212.     rep movsb
  3213. @@FileChar2:
  3214.                 
  3215. ; Get interrupt 2D and 2F vectors.
  3216.     ASSUME    ds:NOTHING,es:NOTHING,fs:ResCode,gs:NOTHING
  3217.     mov    ax, ResCode
  3218.     mov    fs, ax
  3219. GetIntVectors:
  3220.     mov     ax, 352Dh
  3221.     int     21h
  3222.     mov     [WORD OldInt2D], bx
  3223.     mov     [WORD HIGH OldInt2D], es
  3224.     mov     ax, 352Fh
  3225.     int     21h
  3226.     mov     [WORD OldInt2F], bx
  3227.     mov     [WORD HIGH OldInt2F], es
  3228.  
  3229. ; Set new interrupt 2D and 2F vectors.
  3230.     push    ds
  3231.     mov    ax, ResCode
  3232.     mov    ds, ax
  3233.     mov    ax, 252Dh
  3234.     mov    dx, OFFSET Int2DEntry
  3235.     int    21h
  3236.     mov     ax, 252Fh
  3237.     mov     dx, OFFSET Int2FEntry
  3238.     int     21h
  3239.     pop     ds
  3240.  
  3241. ; Terminate and Stay Resident
  3242.     mov     ax, 3100h
  3243.     mov    dx, ResData
  3244.         sub    dx, ResCode
  3245.         add    dx, 16        ; 16 paras for PSP
  3246.         mov    bx, OFFSET EndResData
  3247.         shr    bx, 4
  3248.         add    dx, bx
  3249.         inc    dx
  3250.     int     21h
  3251.  
  3252. @@Exit:
  3253.     mov    ax, 4C00h
  3254.     int    21h
  3255. ;--------
  3256. ; Errors
  3257.  
  3258. errBadDosVer:
  3259.     Abort   0
  3260. errEnvRel:
  3261. errMemRel:
  3262.     Abort    1        ; Malloc error
  3263. errNoSDA:
  3264.     Abort    3        ; Compatibility error
  3265. errNoHPFS:
  3266.     Abort   5
  3267. errNoFreeMux:
  3268.     Abort    14        ; No free multiplex function
  3269. @@errCantUninstall:
  3270.     Abort    11            ; Cannot unload
  3271. @@errNotLoaded:
  3272.     Abort    15            ; Driver not loaded
  3273. ENDP    Main
  3274.  
  3275. ;---------------------------------------------------------------------
  3276. ; Scans all hard disks in the system.
  3277. PROC    ScanDisks STDCALL
  3278.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3279.     LOCAL    @@Drive:BYTE, @@Cyls:WORD, @@Heads:WORD, @@Secs:WORD
  3280.     LOCAL    @@IBM_MS_Ext:WORD
  3281.     push    es fs gs
  3282.     mov    ax, ResCode
  3283.     mov    es, ax
  3284.         mov    fs, ax
  3285.  
  3286.     mov    [@@Drive], 80h
  3287. @@DoDrive:
  3288. ; Get drive type
  3289.     mov    ah, 15h
  3290.     mov    dl, [@@Drive]
  3291.     int    13h
  3292.     jc    @@Done
  3293.     cmp    ah, 03h
  3294.     jne    @@Done
  3295. ; Get drive parameters
  3296.     mov    ah, 08h
  3297.     mov    dl, [@@Drive]
  3298.     int    13h
  3299.     jc    @@Done
  3300.     mov    ax, cx
  3301.     xchg    ah, al
  3302.     shr    ah, 6
  3303.     inc    ax
  3304.     mov    [@@Cyls], ax
  3305.     mov    [BYTE LOW @@Heads], dh
  3306.     mov    [BYTE HIGH @@Heads], 0
  3307.     inc    [@@Heads]
  3308.     and    cx, 3Fh
  3309.     mov    [@@Secs], cx
  3310. ; Check for IBM/MS Extensions
  3311.     mov    [@@IBM_MS_Ext], 0
  3312.     mov    ah, 41h
  3313.     mov    bx, 55AAh
  3314.     mov    dl, [@@Drive]
  3315.     int    13h
  3316.     jc    @@ExtChecked
  3317.     cmp    bx, 0AA55h
  3318.     jne    @@ExtChecked
  3319.     test    cl, 1            ; Extended functions supported
  3320.     jne    @@ExtChecked
  3321.     mov    [@@IBM_MS_Ext], 1
  3322. @@ExtChecked:
  3323. ; Read the first sector just to make sure the drive exists. This is the only
  3324. ; safe way as the drive type/drive param calls may return crazy values.
  3325.     mov    ax, 0201h
  3326.     mov    bx, OFFSET Buf1
  3327.     mov    cx, 1
  3328.     movzx    dx, [@@Drive]
  3329.     int    13h
  3330.     jc    @@Done
  3331.  
  3332.     movzx    dx, [@@Drive]
  3333.     mov    [ExtPartBase], 0
  3334.     call    NEAR ScanPartTbl, Method_CHS,dx,[@@Heads],[@@Secs],[@@IBM_MS_Ext],0,0,1,0 0
  3335.     inc    [@@Drive]
  3336.     jnc    @@DoDrive
  3337.  
  3338. @@Done:
  3339.     pop    gs fs es
  3340.     ret
  3341. ENDP    ScanDisks
  3342.  
  3343. ;---------------------------------------------------------------------
  3344. ; Scans partition tables on a drive.
  3345. ; Args: @@Method    Disk access method to use
  3346. ;    @@Drive        Drive number
  3347. ;    @@nHeads    Number of heads (logical)
  3348. ;    @@nSecs        Number of sectors (logical)
  3349. ;    @@IBM_MS_Ext    IBM/MS Extensions supported
  3350. ;    @@PartCyl    Cylinder of partition table
  3351. ;    @@PartHead    Head of partition table
  3352. ;    @@PartSec    Sector of partition table
  3353. ;    @@PartLBA    LBA of partition table
  3354. ; Return CF=1 if a critical error is encountered (scan should not continue)
  3355. PROC    ScanPartTbl PASCAL
  3356.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3357.     ARG    @@Method,@@Drive,@@nHeads,@@nSecs,@@IBM_MS_Ext,@@PartCyl,@@PartHead,@@PartSec,@@PartLBA:DWORD
  3358.     LOCAL    @@PartOfs:WORD, @@HighPart:BYTE, @@sLBA:DWORD
  3359.     LOCAL    @@sCyl:WORD, @@sHead:WORD, @@sSec:WORD
  3360.     LOCAL    @@eCyl:WORD, @@eHead:WORD, @@eSec:WORD
  3361.  
  3362.     mov    [@@PartOfs], 1BEh
  3363. @@DoEntry:
  3364.     mov    [@@HighPart], 0
  3365.     cmp    [@@Method], Method_CHS
  3366.     je    @@ReadPartCHS
  3367.     cmp    [@@Method], Method_CHSExt
  3368.     je    @@ReadPartCHSExt
  3369.     jmp    @@ReadPartExt
  3370.  
  3371. ; Read partition table using CHS
  3372. @@ReadPartCHS:
  3373.     mov    ax, 0201h
  3374.     mov    bx, OFFSET Buf1
  3375.     mov    ch, [BYTE LOW @@PartCyl]
  3376.     mov    cl, [BYTE HIGH @@PartCyl]
  3377.     shl    cl, 6
  3378.     xor    cl, [BYTE LOW @@PartSec]
  3379.     mov    dh, [BYTE LOW @@PartHead]
  3380.     mov    dl, [BYTE LOW @@Drive]
  3381.     int    13h
  3382.     jc    @@errReadError
  3383.     jmp    @@PartTableRead
  3384.  
  3385. ; Read partition table using extended CHS
  3386. @@ReadPartCHSExt:
  3387.     call    NEAR ReadSectorExtCHS PASCAL, [@@PartLBA],[@@Drive],[@@nHeads],[@@nSecs],es (OFFSET Buf1)
  3388.     jc    @@errReadError
  3389.     jmp    @@PartTableRead
  3390.  
  3391. ; Read partition table using IBM/MS Extensions
  3392. @@ReadPartExt:
  3393.     mov    [DiskAddrPkt1.Count], 1
  3394.     mov    eax, [@@PartLBA]
  3395.     mov    [DWORD LOW DiskAddrPkt1.Sector], eax
  3396.     mov    [WORD LOW DiskAddrPkt1.Buffer], OFFSET Buf1
  3397.     mov    [WORD HIGH DiskAddrPkt1.Buffer], SEG Buf1
  3398.     mov    dl, [BYTE LOW @@Drive]
  3399.     mov    si, OFFSET DiskAddrPkt1
  3400.     mov    ah, 42h
  3401.     int    13h
  3402.     jc    @@errReadError
  3403. @@PartTableRead:
  3404.  
  3405. ; Check partition table signature
  3406.     cmp    [WORD Buf1+510], 0AA55h
  3407.     jne    @@errBadPartTable
  3408.  
  3409.     mov    bx, [@@PartOfs]
  3410.     cmp    [BYTE Buf1+bx+04h], 0        ; Unused entry
  3411.     jz    @@NextEntry
  3412. ; Extract CHS information
  3413.     movzx    ax, [Buf1+bx+01h]
  3414.     mov    [@@sHead], ax
  3415.     movzx    ax, [Buf1+bx+02h]
  3416.     mov    [@@sSec], ax
  3417.     and    [@@sSec], 3Fh
  3418.     shl    ax, 2
  3419.     mov    al, [Buf1+bx+03h]
  3420.     mov    [@@sCyl], ax
  3421.  
  3422.     movzx    ax, [Buf1+bx+05h]
  3423.     mov    [@@eHead], ax
  3424.     movzx    ax, [Buf1+bx+06h]
  3425.     mov    [@@eSec], ax
  3426.     and    [@@eSec], 3Fh
  3427.     shl    ax, 2
  3428.     mov    al, [Buf1+bx+07h]
  3429.     mov    [@@eCyl], ax
  3430. ; Calculate partition size from CHS information to determine if high
  3431.     movzx    eax, [@@eCyl]
  3432.     sub    ax, [@@sCyl]
  3433.     mul    [@@nHeads]
  3434.     shl    edx, 16
  3435.     add    eax, edx
  3436.     movzx    edx, [@@nSecs]
  3437.     mul    edx
  3438.     mov    ecx, eax
  3439.     movzx    eax, [BYTE LOW @@eHead]
  3440.     sub    al, [BYTE LOW @@sHead]
  3441.     mul    [BYTE LOW @@nSecs]
  3442.     add    al, [BYTE LOW @@eSec]
  3443.     adc    ah, 0
  3444.     sub    al, [BYTE LOW @@sSec]
  3445.     sbb    ah, 0
  3446.     add    ecx, eax
  3447.     inc    ecx            ; ECX = partition size
  3448.     cmp    ecx, [DWORD Buf1+bx+0Ch]
  3449.     je    @@PartSizeDone
  3450.     mov    [@@HighPart], 1
  3451.     cmp    ecx, 1
  3452.     je    @@PartSizeDone
  3453.     cmp    [Buf1+bx+04h], 05h
  3454.     jne    @@PartSizeDone
  3455.     mov    [@@HighPart], 0        ; Extended partition, partially high
  3456. @@PartSizeDone:
  3457.  
  3458.     mov    al, [Buf1+bx+04h]    ; Partition type
  3459.     cmp    al, 05h
  3460.     je    @@ExtPart
  3461.     cmp    al, 07h
  3462.     je    @@HPFSPart
  3463.     cmp    al, 17h
  3464.     je    @@HPFSPart
  3465.     jmp    @@NextEntry
  3466.  
  3467. @@ExtPart:
  3468.     mov    eax, [DWORD Buf1+bx+08h]
  3469.     add    eax, [ExtPartBase]    ; Relative to first extended partition
  3470.     mov    [@@sLBA], eax        ; LBA of partition
  3471.     cmp    [ExtPartBase], 0
  3472.     jnz    @@ExtBaseDone
  3473.     mov    [ExtPartBase], eax    ; This is the first ext part
  3474. @@ExtBaseDone:
  3475.  
  3476.     cmp    [@@HighPart], 0
  3477.     jnz    @@ExtPartHigh
  3478.     call    NEAR ScanPartTbl, Method_CHS,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],[@@sCyl],[@@sHead],[@@sSec],[@@sLBA]
  3479.     jc    @@Done
  3480.     jmp    @@NextEntry
  3481. @@ExtPartHigh:
  3482.     cmp    [@@IBM_MS_Ext], 0
  3483.     jnz    @@ExtPartIBMExt
  3484.     cmp    [UseExtCHS], 0
  3485.     jz    @@NextEntry
  3486.     test    [BYTE LOW @@nHeads], 0C0h
  3487.     jnz    @@NextEntry        ; Must have at most 63 heads
  3488.     call    NEAR CheckCylNumber, [@@sLBA], [@@nHeads], [@@nSecs]
  3489.     jnz    @@NextEntry
  3490.     call    NEAR ScanPartTbl, Method_CHSExt,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],0,0,0,[@@sLBA]
  3491.     jc    @@Done
  3492.     jmp    @@NextEntry
  3493. @@ExtPartIBMExt:
  3494.     call    NEAR ScanPartTbl, Method_Ext,[@@Drive],[@@nHeads],[@@nSecs],[@@IBM_MS_Ext],0,0,0,[@@sLBA]
  3495.     jc    @@Done
  3496.     jmp    @@NextEntry
  3497.  
  3498. @@HPFSPart:
  3499.     mov    eax, [@@PartLBA]    ; Relative to current ext part
  3500.     add    eax, [DWORD Buf1+bx+08h]
  3501.     mov    [@@sLBA], eax        ; LBA of partition
  3502.  
  3503.     cmp    [@@HighPart], 0
  3504.     jnz    @@HPFSPartHigh
  3505.     call    NEAR CheckHPFSPart, Method_CHS,[@@Drive],[@@nHeads],[@@nSecs],[@@sCyl],[@@sHead],[@@sSec],[@@sLBA]
  3506.     jc    @@Done
  3507.     jmp    @@NextEntry
  3508. @@HPFSPartHigh:
  3509.     cmp    [@@IBM_MS_Ext], 0
  3510.     jnz    @@HPFSPartIBMExt
  3511.     cmp    [UseExtCHS], 0
  3512.     jz    @@NextEntry
  3513.     test    [BYTE LOW @@nHeads], 0C0h
  3514.     jnz    @@NextEntry        ; Must have at most 63 heads
  3515.     mov    eax, [@@sLBA]        ; Check that we can read last sector
  3516.     add    eax, [DWORD Buf1+bx+0Ch]
  3517.     dec    eax
  3518.     call    NEAR CheckCylNumber, eax, [@@nHeads], [@@nSecs]
  3519.     jnz    @@NextEntry
  3520.         call    NEAR CheckHPFSPart, Method_CHSExt,[@@Drive],[@@nHeads],[@@nSecs],0,0,0,[@@sLBA]
  3521.     jc    @@Done
  3522.     jmp    @@NextEntry
  3523. @@HPFSPartIBMExt:
  3524.     call    NEAR CheckHPFSPart, Method_Ext,[@@Drive],[@@nHeads],[@@nSecs],0,0,0,[@@sLBA]
  3525.     jc    @@Done
  3526.     jmp    @@NextEntry
  3527.  
  3528. @@NextEntry:
  3529.     add    [@@PartOfs], 10h
  3530.     cmp     [@@PartOfs], 1FEh
  3531.     jb      @@DoEntry
  3532.     clc
  3533.     jmp    @@Done
  3534.  
  3535. @@errReadError:
  3536.     mov    dx, OFFSET MsgDiskError
  3537.     mov    ah, 9
  3538.     int    21h
  3539.     stc
  3540.     jmp    @@Done
  3541.  
  3542. @@errBadPartTable:
  3543.     mov    dx, OFFSET MsgBadPartTable
  3544.     mov    ah, 9
  3545.     int    21h
  3546.     stc
  3547.     ; FALL THROUGH to @@Done
  3548.  
  3549. @@Done:
  3550.     ret
  3551. ENDP    ScanPartTbl
  3552.  
  3553. ;---------------------------------------------------------------------
  3554. ; Checks and possibly installs an HPFS partition. Uses Buf2
  3555. ; Args: @@Method    Disk access method to use
  3556. ;    @@PhysDrive    Drive number
  3557. ;    @@nHeads    Number of heads (logical)
  3558. ;    @@nSecs        Number of sectors (logical)
  3559. ;    @@sCyl        Starting cylinder
  3560. ;    @@sHead        Starting head
  3561. ;    @@sSec        Starting sector
  3562. ;    @@sLBA        Starting LBA
  3563. ; Returns CF=1 if a critical error occurs.
  3564. PROC    CheckHPFSPart PASCAL
  3565.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3566.     ARG    @@Method,@@PhysDrive,@@nHeads,@@nSecs,@@sCyl,@@sHead,@@sSec,@@sLBA:DWORD
  3567.     LOCAL    @@Drive:BYTE
  3568.     push    ds es fs gs
  3569.     pushad
  3570.     cmp    [@@Method], Method_CHS
  3571.     je    @@ReadCHS
  3572.     cmp    [@@Method], Method_CHSExt
  3573.     je    @@ReadCHSExt
  3574.     jmp    @@ReadExt
  3575.  
  3576. ; Read boot sector using CHS
  3577. @@ReadCHS:
  3578.     mov    ax, 0201h
  3579.     mov    bx, OFFSET Buf2
  3580.     mov    ch, [BYTE LOW @@sCyl]
  3581.     mov    cl, [BYTE HIGH @@sCyl]
  3582.      shl    cl, 6
  3583.     xor    cl, [BYTE LOW @@sSec]
  3584.     mov    dh, [BYTE LOW @@sHead]
  3585.     mov    dl, [BYTE LOW @@PhysDrive]
  3586.     int    13h
  3587.     jc    @@errReadError
  3588.     jmp    @@BootRead
  3589.  
  3590. ; Read boot sector using extended CHS
  3591. @@ReadCHSExt:
  3592.         call    NEAR ReadSectorExtCHS PASCAL, [@@sLBA],[@@PhysDrive],[@@nHeads],[@@nSecs],es (OFFSET Buf2)
  3593.     jc    @@errReadError
  3594.     jmp    @@BootRead
  3595.  
  3596. ; Read boot sector using IBM/MS Extensions
  3597. @@ReadExt:
  3598.     mov    [DiskAddrPkt1.Count], 1
  3599.     mov    eax, [@@sLBA]
  3600.     mov    [DWORD LOW DiskAddrPkt1.Sector], eax
  3601.     mov    [WORD LOW DiskAddrPkt1.Buffer], OFFSET Buf2
  3602.     mov    [WORD HIGH DiskAddrPkt1.Buffer], SEG Buf2
  3603.     mov    dl, [BYTE LOW @@PhysDrive]
  3604.     mov    si, OFFSET DiskAddrPkt1
  3605.     mov    ah, 42h
  3606.     int    13h
  3607.     jc    @@errReadError
  3608.  
  3609. @@BootRead:
  3610. ; Check the HPFS signature
  3611.     cmp     [WORD Buf2+36h], 'PH'
  3612.     jne     @@Done
  3613.     cmp     [WORD Buf2+38h], 'SF'
  3614.     jne     @@Done
  3615. ; HPFS partition found.
  3616.     inc    [PartCount]
  3617.     movzx    bx, [PartCount]
  3618.     mov    dl, [Partitions+bx]
  3619.     mov    [@@Drive], dl
  3620.     mov    [Partitions+bx], 0FFh        ; Partition found
  3621.     cmp    dl, 0FFh
  3622.     je    @@Done                ; Don't install this partition
  3623. ; Check that partition is not already installed
  3624.     call    NEAR IsInstalledPart, bx
  3625.     or    ah, ah
  3626.     jnz    @@errPartInstalled
  3627.  
  3628.     les     bx, [LstOfLst]
  3629.     ASSUME    es:NOTHING
  3630.     les     si, [es:bx+16h]        ; CDS array
  3631.     cmp    [@@Drive], 0FEh
  3632.     je    @@ScanCDS        ; Find first free drive letter
  3633.     mov     ah, 36h                 ; Get disk free space
  3634.     mov     dl, [@@Drive]
  3635.     inc     dl
  3636.     int     21h
  3637.     cmp     ax, 0FFFFh
  3638.     jne     @@errDrvUsed
  3639.     mov     al, [BYTE CDSSize]
  3640.     mov     cl, [@@Drive]
  3641.     mul     cl
  3642.     mov     si, ax                  ; Points to CDS for our drive
  3643.     cmp     cl, [LastDrive]
  3644.     jb    @@FoundCDS
  3645.     jmp     @@errInvDrv
  3646.  
  3647. ; Search the CDS array, look for an unused CDS.
  3648. ; ES:SI -> CDS.
  3649. @@ScanCDS:
  3650.     mov    [@@Drive], 0
  3651. @@ScanCDS1:
  3652.     cmp    [DrDos], 0
  3653.     jz    @@ScanCDS2
  3654.     cmp    [WORD es:si+43h], 0    ; DR-DOS
  3655.     jmp    @@ScanCDS3
  3656. @@ScanCDS2:
  3657.     test    [WORD es:si+43h], 0C000h  ; Mask bits 15 and 14 of drv attributes
  3658. @@ScanCDS3:
  3659.     jz      @@FoundCDS                ; If 0, then drive is invalid = free
  3660.     add     si, [CDSSize]             ; Point to next CDS
  3661.     inc     [@@Drive]
  3662.     mov    cl, [@@Drive]
  3663.     cmp     cl, [LastDrive]
  3664.     jb      @@ScanCDS1                ; Go to next CDS entry
  3665.     jmp    @@errOutOfDrv
  3666.  
  3667. @@FoundCDS:
  3668. ; Allocate memory for resident data
  3669.     mov    bx, OFFSET EndResData
  3670.     shr    bx, 4
  3671.     inc    bx
  3672.     mov    ah, 48h
  3673.     int    21h
  3674.     jc    @@errMallocErr
  3675.     movzx    bx, [@@Drive]
  3676.     shl    bx, 1
  3677.     mov    [DataSegs+bx], ax    ; Save ResData segment
  3678.     mov    gs, ax
  3679.     ASSUME    gs:ResData
  3680. ; Clear resident data.
  3681.     push    es
  3682.     mov    es, ax
  3683.     xor    al, al
  3684.     cld
  3685.     mov    cx, OFFSET EndResData
  3686.     xor    di, di
  3687.     rep stosb
  3688.     pop    es
  3689. ; Set resident data
  3690.     mov    al, [BYTE LOW @@PhysDrive]
  3691.     mov    [PhysDrv], al
  3692.     mov    ax, [@@nHeads]
  3693.     mov    [nHeads], ax
  3694.     mov    al, [BYTE LOW @@nSecs]
  3695.     mov    [nSecs], al
  3696.     mov    al, [PartCount]
  3697.     mov    [PartitionNr], al
  3698.     mov    al, [BYTE LOW @@Method]
  3699.     mov    [AccessMethod], al
  3700.     mov    eax, [@@sLBA]
  3701.     mov    [LBAstart], eax
  3702.         call    InitResData
  3703. ; Set CDS fields
  3704.     mov    cl, [@@Drive]
  3705.     add     cl, 'A'
  3706.     mov     [MsgDrvLetter], cl
  3707.     cmp    [DrDos], 0
  3708.     jz    @@SetCDS
  3709.     mov    [WORD es:si+43h], 8000h    ; DR-DOS
  3710.     jmp    @@CDSSet
  3711. @@SetCDS:
  3712.     or      [WORD es:si+43h], 0C000h ; Flags+Physical bits on = Netwrk drive
  3713. @@CDSSet:
  3714.     mov     [es:si], cl
  3715.     mov     [WORD es:si+1], '\:'
  3716.     mov     [BYTE es:si+3], 0
  3717.     mov     [WORD es:si+4Fh], 2 ; Offset of backslash
  3718.     mov    [Installed], 1        ; Drive successfully installed
  3719. ; Partition installed - print message
  3720.     mov    dx, OFFSET MsgInstalled
  3721.     mov    ah, 9
  3722.     int    21h
  3723.     jmp    @@Done
  3724.  
  3725. @@errReadError:
  3726.     mov    dx, OFFSET MsgDiskError
  3727.     mov    ah, 9
  3728.     int    21h
  3729.     jmp    @@Done
  3730. @@errDrvUsed:
  3731.     mov    dl, [@@Drive]
  3732.     add    dl, 'A'
  3733.     mov    [MsgDrvUsedLetter], dl
  3734.     mov    dx, OFFSET MsgDrvUsed
  3735.     mov    ah, 9
  3736.     int    21h
  3737.     mov    [ErrSignaled], 1
  3738.     jmp    @@Done
  3739. @@errInvDrv:
  3740.     mov    dl, [@@Drive]
  3741.     add    dl, 'A'
  3742.     mov    [MsgInvDrvLetter], dl
  3743.     mov    dx, OFFSET MsgInvDrv
  3744.     mov    ah, 9
  3745.         int    21h
  3746.     mov    [ErrSignaled], 1
  3747.         jmp    @@Done
  3748. @@errPartInstalled:
  3749.     movzx    ax, [PartCount]
  3750.     mov    dl, 10
  3751.     div    dl
  3752.     or    al, al
  3753.     jz    @@errPartInstalled1    ; Leave 00h if first digit 0.
  3754.     add    al, 30h
  3755. @@errPartInstalled1:
  3756.     add    ah, 30h
  3757.     mov    [WORD MsgPartInstalledNr], ax
  3758.     mov    dx, OFFSET MsgPartInstalled
  3759.     mov    ah, 9
  3760.     int    21h
  3761.     mov    [ErrSignaled], 1
  3762.         jmp    @@Done
  3763. @@errOutOfDrv:
  3764.     mov    dx, OFFSET MsgNoAvailDrvLetter
  3765.     mov    ah, 9
  3766.     int    21h
  3767.     mov    [ErrSignaled], 1
  3768.     jmp    @@Fail
  3769. @@errMallocErr:
  3770.     mov    dx, OFFSET MsgMallocErr
  3771.     mov    ah, 9
  3772.     int    21h
  3773.     mov    [ErrSignaled], 1
  3774.     jmp    @@Fail
  3775.  
  3776. @@Fail:    stc
  3777.     jmp    @@Exit
  3778. @@Done:
  3779.     clc
  3780. @@Exit:
  3781.     popad
  3782.     pop    gs fs es ds
  3783.     ret
  3784. ENDP    CheckHPFSPart
  3785.  
  3786. ;---------------------------------------------------------------------
  3787. ; Read a sector given by LBA using extended CHS addressing.
  3788. PROC    ReadSectorExtCHS PASCAL
  3789.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3790.     ARG    @@Sector:DWORD, @@Drive:WORD, @@nHeads:WORD, @@nSecs:WORD, @@Buf:DWORD
  3791.     pushad
  3792.     push    es
  3793.     mov    ecx, [@@Sector]
  3794.     mov    ax, [@@nSecs]
  3795.     mul    [@@nHeads]
  3796.     mov     bx, ax          ; Sectors/track * heads
  3797.     mov     ax, cx
  3798.     mov     edx, ecx
  3799.     shr     edx, 16         ; DX:AX = logical sector #
  3800.     div     bx
  3801.     push    ax              ; Cylinder
  3802.     mov     ax, dx
  3803.     div     [BYTE LOW @@nSecs]
  3804.     movzx   dx, al          ; Head
  3805.     mov     cl, ah          ; Sector
  3806.     inc    cl
  3807.     pop     ax              ; Cylinder
  3808.     mov    dh, dl        ; Head
  3809.     mov     ch, al
  3810.     xor     al, al
  3811.     shr     ax, 2
  3812.     or      cl, al          ; bits 8 and 9 of cyl. number go here
  3813.         xor     al, al
  3814.         shr     ax, 2
  3815.         or      dh, al          ; bits 10 and 11 of cyl (BIOS extension)
  3816.  
  3817.     mov    ax, 0201h
  3818.     les    bx, [@@Buf]
  3819.     mov    dl, [BYTE LOW @@Drive]
  3820.     int    13h
  3821.     pop    es
  3822.     popad
  3823.     ret
  3824. ENDP    ReadSectorExtCHS
  3825.  
  3826. ;---------------------------------------------------------------------
  3827. ; Determines the cylinder number of a given logical sector and returns
  3828. ; ZF=1 if it is less than 4096, which means it can be read with 
  3829. ; extended CHS addressing.
  3830. PROC    CheckCylNumber PASCAL
  3831.     ASSUME  ds:DSeg,es:ResCode,fs:ResCode,gs:NOTHING
  3832.     ARG    @@Sector:DWORD, @@nHeads:WORD, @@nSecs:WORD
  3833.     pushad
  3834.     mov    ecx, [@@Sector]
  3835.     mov    ax, [@@nSecs]
  3836.     mul    [@@nHeads]
  3837.     mov     bx, ax          ; Sectors/track * heads
  3838.     mov     ax, cx
  3839.     mov     edx, ecx
  3840.     shr     edx, 16         ; DX:AX = logical sector #
  3841.     div     bx
  3842.     test    ax, 0F000h
  3843.     popad
  3844.     ret
  3845. ENDP    CheckCylNumber
  3846.  
  3847. ;---------------------------------------------------------------------
  3848. ; Initialize resident data. Assumes GS=ResData
  3849. PROC    InitResData STDCALL
  3850.     ASSUME    ds:ResCode,es:ResData,fs:NOTHING,gs:ResData
  3851.     LOCAL    @@BitmapTable:DWORD
  3852.         pushad
  3853.         push    ds es fs gs
  3854.     mov    ax, ResCode
  3855.     mov    ds, ax
  3856.     push    gs
  3857.     pop    es
  3858. ; Read the boot sector
  3859.     call    FAR ThunkReadSector PASCAL, LARGE 0, 1, ds (OFFSET Buf1)
  3860.     jc      @@ReadError
  3861.     mov     cx, 11
  3862.     mov     si, OFFSET Buf1+2Bh
  3863.     mov     di, OFFSET Volabel
  3864.     rep movsb
  3865.     mov    al, [BYTE Buf1+15h]        ; Media ID byte
  3866.     mov    [MediaID], al
  3867. ; Read the SuperBlock
  3868.     call    FAR ThunkReadSector PASCAL, LARGE 16, 1, ds (OFFSET Buf1)
  3869.     jc      @@ReadError
  3870.     mov     eax, [DWORD Buf1+0Ch]    ; Root dir fnode
  3871.     mov     [CDFNode], eax
  3872.     mov     [RootFNode], eax
  3873.     mov    eax, [DWORD Buf1+10h]    ; Partition size in sectors
  3874.     mov    [TotalSectors], eax
  3875.         mov     eax, [DWORD Buf1+18h]
  3876.         mov     [@@BitmapTable], eax
  3877. ; Scan the free space bitmaps and count free sectors.
  3878.         mov     edx, [TotalSectors]
  3879.         add     edx, 3FFFh
  3880.         shr     edx, 14         ; Number of bands
  3881.         shl     dx, 2
  3882.         xor     bx, bx          ; offset into bitmap table
  3883.         xor     ecx, ecx        ; bit count
  3884. @@DoBand:
  3885. ; Read free space bitmap table
  3886.         call    FAR ThunkReadSector PASCAL, [@@BitmapTable], 4, ds (OFFSET Buf1)
  3887.         jc      @@ReadError
  3888. ; Read free space bitmaps
  3889.         call    FAR ThunkReadSector PASCAL, [DWORD Buf1+bx], 4, ds (OFFSET Buf1)
  3890.         jc      @@ReadError
  3891.     call    CountBits
  3892.         add     ecx, eax
  3893.         add     bx, 4
  3894.         cmp     bx, dx
  3895.         jne     @@DoBand
  3896.         mov     [FreeSectors], ecx
  3897.  
  3898.            pop    gs fs es ds
  3899.         popad
  3900.     ret
  3901. @@ReadError:
  3902.     Abort    4
  3903. ENDP    InitResData                
  3904.  
  3905. ;---------------------------------------------------------------------
  3906. ; Count number of set bits in Buf. Assumes DS=ResCode. Returns
  3907. ; number of set bits in EAX.
  3908. PROC    CountBits
  3909.     ASSUME    ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
  3910.     push    cx dx si
  3911.     xor    dx, dx
  3912.     mov    si, OFFSET Buf1
  3913.     mov    cx, 4*512
  3914.     cld
  3915. @@Bytes:
  3916.     lodsb
  3917.     push    cx
  3918.     mov    cx, 8
  3919. @@Bits: shr    al, 1
  3920.     adc    dx, 0
  3921.     loop    @@Bits
  3922.     pop    cx
  3923.     loop    @@Bytes
  3924.     movzx    eax, dx
  3925.     pop    si dx cx
  3926.     ret
  3927. ENDP    CountBits
  3928.  
  3929. ;---------------------------------------------------------------------
  3930. ; Write error message BX, release memory and exit
  3931. PROC    AbortMsg
  3932.     ASSUME  cs:CSeg,ds:DSeg,es:NOTHING,fs:ResCode,gs:NOTHING
  3933.     push    bx
  3934.     mov     ax, DSeg
  3935.     mov     ds, ax
  3936.     mov     ax, ResCode
  3937.     mov     fs, ax
  3938.     shl     bx, 1
  3939.     mov     dx, [ErrMsgTbl+bx]
  3940.     mov     ah, 9
  3941.     int     21h
  3942. ; Deallocate XMS
  3943.     cmp     [CacheOn], 0
  3944.     jz      @@Exit
  3945.     mov     cl, [XMSBlocks]
  3946.     xor     ch, ch
  3947.     jcxz    @@1
  3948.     mov     ah, 0Ah
  3949.     mov     dx, [hCacheSectors]
  3950.     call    [XMSEntry]
  3951.     dec     cx
  3952.     jcxz    @@1
  3953.     mov     ah, 0Ah
  3954.     mov     dx, [hCacheLists]
  3955.     call    [XMSEntry]
  3956.     dec     cx
  3957.     jcxz    @@1
  3958.     mov     ah, 0Ah
  3959.     mov     dx, [hHashTable]
  3960.     call    [XMSEntry]
  3961. ; Release allocated ResData blocks.
  3962. @@1:    xor    bx, bx
  3963.     mov    cx, 26
  3964. @@2:    mov    ax, [DataSegs+bx]
  3965.     or    ax, ax
  3966.     jz    @@3
  3967.     mov    es, ax
  3968.     mov    ah, 49h
  3969.     push    bx cx
  3970.     int    21h
  3971.     pop    cx bx
  3972. @@3:    add    bx, 2
  3973.     loop    @@2    
  3974. @@Exit:
  3975.     pop    ax
  3976.     mov     ah, 4Ch
  3977.     int     21h
  3978. ENDP    AbortMsg
  3979.  
  3980. ;---------------------------------------------------------------------
  3981. ; Check if iHPFS is alredy loaded for a partition. Returns
  3982. ; AH=00h if not installed, 01h if installed
  3983. ; DL=drive number if partition installed.
  3984. PROC    IsInstalledPart PASCAL
  3985.     ARG    @@Part
  3986.     LOCAL    @@Func:BYTE, @@Result, @@Drive:BYTE
  3987.         pushad
  3988. ; Scan through INT 2D functions 00h-0FFh.
  3989.     mov    [@@Func], 0
  3990. @@CallMultiplex:
  3991.     mov    ah, [@@Func]
  3992.     xor    al, al        ; Installation check
  3993.     int     2Dh
  3994.     cmp    al, 0FFh
  3995.     jne    @@NextMultiplex
  3996.     mov    bx, ResCode    ; Compare signature strings
  3997.     mov    si, OFFSET AMISSign
  3998.     push    ds
  3999.     mov    ds, bx
  4000.     mov    es, dx
  4001.     mov    cx, 4
  4002.     cld
  4003.     repe cmpsd
  4004.     pop    ds
  4005.     jne    @@NextMultiplex
  4006. ; Installed iHPFS found.
  4007.     call    NEAR QueryPart, [@@Part], es
  4008.     mov    [@@Result], ax
  4009.     mov    [@@Drive], dl
  4010.     or    ah, ah
  4011.     jnz    @@Done        ; Partition found, exit.
  4012. @@NextMultiplex:
  4013.     add    [@@Func], 1
  4014.     jnz    @@CallMultiplex
  4015.     mov    [@@Result], 0FFh ; Partition not found.
  4016.     mov    [@@Drive], 0FFh
  4017. @@Done: popad
  4018.     mov    ax, [@@Result]
  4019.     mov    dl, [@@Drive]
  4020.     ret
  4021. ENDP    IsInstalledPart
  4022.  
  4023. ;---------------------------------------------------------------------
  4024. ; Uninstall driver.
  4025. PROC    UninstallDriver PASCAL
  4026.     ARG    @@ResCode    ; ResCode of driver to uninstall
  4027.     LOCAL    @@PSP
  4028.     ASSUME    ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
  4029.         pushad
  4030.     push    ds es gs
  4031.     mov    ds, [@@ResCode]
  4032. ; Release XMS memory
  4033.     cmp     [CacheOn], 0
  4034.     jz      @@XMSReleased
  4035.     mov     [CacheOn], 0
  4036.     mov     ah, 0Ah
  4037.     mov     dx, [hHashTable]
  4038.     call    [XMSEntry]
  4039.     mov     ah, 0Ah
  4040.     mov     dx, [hCacheLists]
  4041.     call    [XMSEntry]
  4042.     mov     ah, 0Ah
  4043.     mov     dx, [hCacheSectors]
  4044.     call    [XMSEntry]
  4045. @@XMSReleased:
  4046.  
  4047. ; Disconnect drives
  4048.     mov    cx, -1
  4049. @@Remove:
  4050.     inc    cx
  4051.         cmp    cx, 26
  4052.         je    @@DrivesDisconnected
  4053.     mov    bx, cx
  4054.     shl    bx, 1
  4055.     cmp    [DataSegs+bx], 0
  4056.         jz    @@Remove
  4057.         call    NEAR RemoveDrv, cx, ds
  4058.         jmp    @@Remove
  4059.         
  4060. @@DrivesDisconnected:
  4061. ; Get PSP.
  4062.     mov    ah, 51h
  4063.     int    21h
  4064.     mov    [@@PSP], bx    ; Save old PSP
  4065. ; Set PSP to resident driver. This is so the memory block retains its old owner.
  4066.     mov    bx, [PSP]
  4067.     mov     ah, 50h
  4068.     int     21h
  4069.     mov     es, bx
  4070.     ASSUME  es:NOTHING
  4071. ; See if interrupt vectors have been hooked by another TSR
  4072.     xor    ax, ax
  4073.     mov    gs, ax
  4074.     mov    ax, [@@ResCode]
  4075.     cmp    [WORD LOW DWORD gs:2Dh*4], OFFSET Int2DEntry
  4076.     jne    @@Resize
  4077.     cmp    [WORD HIGH DWORD gs:2Dh*4], ax
  4078.     jne    @@Resize
  4079.     cmp    [WORD LOW DWORD gs:2Fh*4], OFFSET Int2FEntry
  4080.     jne    @@Resize
  4081.     cmp    [WORD HIGH DWORD gs:2Fh*4], ax
  4082.     jne    @@Resize
  4083. ; Restore interrupt vectors
  4084.     push    ds
  4085.     pop    gs
  4086.     ASSUME    gs:ResCode
  4087.     lds    dx, [OldInt2D]
  4088.     ASSUME    ds:NOTHING
  4089.     mov    ax, 252Dh
  4090.     int    21h
  4091.     lds    dx, [OldInt2F]
  4092.     mov    ax, 252Fh
  4093.     int    21h
  4094.     push    gs
  4095.     pop    ds
  4096.     ASSUME    ds:ResCode
  4097. ; Release memory block
  4098.     mov    ah, 49h
  4099.     int    21h    
  4100.     jmp    @@MemReleased
  4101. ; Resize memory block       
  4102. @@Resize:
  4103.     mov     bx, OFFSET EndUninstalledCode
  4104.     shr    bx, 4
  4105.     add    bx, 17        ; Paragraphs to keep (code+PSP+1)
  4106.     mov     ah, 4Ah
  4107.     int     21h
  4108. ; Patch in far jump into driver's interrupt code to chain to original handler.
  4109.     mov    [WORD Int2DEntry], 0EA90h    ; NOP and JMP FAR
  4110.     mov    [WORD Int2FEntry], 0EA90h
  4111.  
  4112. @@MemReleased:
  4113. ; Back to original PSP       
  4114.     mov    bx, [@@PSP]
  4115.     mov     ah, 50h
  4116.     int     21h
  4117.  
  4118.     pop    gs es ds
  4119.         popad
  4120.     mov    al, 0FFh
  4121.     ret
  4122. ENDP    UninstallDriver
  4123.  
  4124. ;---------------------------------------------------------------------
  4125. ; Query logical drive connected.
  4126. ; Return: AH=Install status (00h Not installed, 01h Installed)
  4127. PROC    QueryDrive PASCAL
  4128.     ARG    @@Drive, @@ApiFunc
  4129.     ASSUME    ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
  4130.     push    bx
  4131.     mov    ah, [BYTE LOW @@ApiFunc]
  4132.     mov    al, 10h        ; Query drive
  4133.     mov    bx, [@@Drive]
  4134.     int    2Dh
  4135.     pop    bx
  4136.         ret
  4137. ENDP    QueryDrive
  4138.  
  4139. ;---------------------------------------------------------------------
  4140. ; Query HPFS partition connected.
  4141. ; Return: AH=Install status (00h Not installed, 01h Installed)
  4142. ;         DL=Drive letter associated with partition (if installed)
  4143. PROC    QueryPart PASCAL
  4144.     ARG    @@PartNum, @@ResCodeSeg
  4145.     ASSUME    ds:ResCode,es:NOTHING,fs:NOTHING,gs:NOTHING
  4146.     push    bx cx dx
  4147.     push    ds es
  4148.     mov    ds, [@@ResCodeSeg]
  4149.     mov    dx, -1
  4150. @@1:
  4151.     inc    dx
  4152.     cmp    dx, 26
  4153.     je    @@PartNotFound
  4154.     movzx    bx, dl
  4155.     shl    bx, 1
  4156.     mov    bx, [DataSegs+bx]
  4157.     or    bx, bx
  4158.     je    @@1
  4159.     mov    es, bx
  4160.     ASSUME    es:ResData
  4161.     mov    cl, [BYTE LOW @@PartNum]
  4162.     cmp    [PartitionNr], cl
  4163.     jne    @@1
  4164.     ASSUME    es:NOTHING
  4165.     mov    ax, 01FFh
  4166.     jmp    @@Done
  4167. @@PartNotFound:
  4168.     mov    ax, 0FFh
  4169. @@Done:
  4170.     pop    es ds
  4171.     pop    dx cx bx
  4172.     ret
  4173. ENDP    QueryPart
  4174.  
  4175. ;---------------------------------------------------------------------
  4176. ; Remove a drive.
  4177. ; Return: AH=result 
  4178. ;    00h = Uninstalled
  4179. ;    01h = Drv not installed
  4180. ;    02h = Failed for other reason
  4181. PROC    RemoveDrv PASCAL
  4182.     ARG    @@Drive, @@ResCodeSeg
  4183.     LOCAL    @@RetValue
  4184.     ASSUME    ds:ResCode,es:NOTHING,fs:DSeg,gs:NOTHING
  4185.         pushad
  4186.         push    ds es fs
  4187.     mov    ax, DSeg
  4188.     mov    fs, ax
  4189.     mov    ds, [@@ResCodeSeg]
  4190. ; Release resident data segment
  4191.     movzx    bx, [BYTE LOW @@Drive]
  4192.         shl    bx, 1
  4193.     mov    dx, [DataSegs+bx]
  4194.         or    dx, dx
  4195.         jz    @@NotInstalled
  4196.     mov    [DataSegs+bx], 0
  4197.         mov    es, dx
  4198.         mov    ah, 49h
  4199.         int    21h
  4200. ; Patch CDS
  4201.         mov    ah, 52h
  4202.         int    21h        ; Get List of Lists in ES:BX
  4203.         les     bx, [es:bx+16h] ; CDS array
  4204.     mov     al, [BYTE LOW @@Drive]
  4205.         mov     dl, [BYTE CDSSize]
  4206.     mul     dl
  4207.     add     bx, ax          ; CDS entry for drive
  4208.     mov    [WORD es:bx+43h], 0
  4209.     mov    [@@RetValue], 0FFh
  4210.     jmp    @@Exit
  4211.         
  4212. @@NotInstalled:
  4213.     mov    [@@RetValue], 01FFh
  4214. @@Exit:    pop    fs es ds
  4215.         popad
  4216.     mov    ax, [@@RetValue]
  4217.         ret
  4218. ENDP    RemoveDrv
  4219.  
  4220. ;---------------------------------------------------------------------
  4221. ; Parse command line.
  4222. PROC    ParseCmdLine STDCALL
  4223.     LOCAL   @@LastByte, @@CacheOpt:BYTE, @@DrvSpecd, @@DriveNo:BYTE
  4224.     LOCAL    @@PartNum:BYTE, @@UninstallOpt:BYTE
  4225.         ASSUME  ds:DSeg,es:NOTHING,fs:ResCode
  4226.     mov    [@@CacheOpt], 0        ; /C option flag
  4227.     mov    [@@UninstallOpt], 0    ; /U option flag
  4228.     mov    [@@DrvSpecd], 0
  4229.     mov     es, [PSP]
  4230.     movzx   ax, [BYTE es:80h]
  4231.     cmp     al, 2
  4232.     jb      @@Done
  4233.     add     al, 80h
  4234.     mov     [@@LastByte], ax
  4235. ; Find first non-space character
  4236.     mov     di, 81h
  4237. @@Next:
  4238.     mov     cx, [@@LastByte]
  4239.     sub     cx, di
  4240.     jc      @@Done
  4241.     inc     cx
  4242.     mov     al, ' '
  4243.     cmp     al, ' '         ; Set zero flag
  4244.     repe scasb
  4245.     je      @@Done
  4246.     dec     di
  4247. ; Get char and convert to upper case
  4248.     mov     dl, [es:di]
  4249.     mov     ax, 6520h
  4250.     int     21h
  4251. ; Check for drive spec 'A'..'Z'
  4252.     cmp     dl, 'A'
  4253.     jb      @@Parse1
  4254.     cmp     dl, 'Z'
  4255.     ja      @@Parse1
  4256. ; Drive spec.
  4257.     inc     di
  4258.     mov     al, ':'                 ; Match a colon
  4259.     scasb
  4260.     jne     @@errBadOption
  4261.     sub     dl, 'A'                 ; Drive number
  4262.     mov     [@@DriveNo], dl
  4263. ; Check for partition number '1'..'9'
  4264.     cmp    di, [@@LastByte]
  4265.     ja    @@SetDrvLetter        ; Save drive letter in list
  4266.     mov     al, [es:di]
  4267.     cmp    al, ' '
  4268.     je    @@SetDrvLetter
  4269.     cmp     al, '1'
  4270.     jb      @@errBadOption
  4271.     cmp     al, '9'
  4272.     ja      @@errBadOption
  4273. ; Partition number
  4274.     mov     [Install], 1            ; Partition number - installing
  4275.     inc     di
  4276.     sub     al, '0'
  4277.     mov     [@@PartNum], al
  4278.     cmp    di, [@@LastByte]
  4279.     ja    @@SetPart
  4280.     mov    al, [es:di]        ; Second digit
  4281.     cmp    al, ' '
  4282.     je    @@SetPart
  4283.     cmp    al, '0'
  4284.     jb    @@errBadOption
  4285.     cmp    al, '9'
  4286.     ja    @@errBadOption
  4287.     inc    di
  4288.     sub    al, '0'
  4289.     mov    bl, [@@PartNum]
  4290.     shl    bl, 3            ; Multiply by 10
  4291.     add    bl, [@@PartNum]
  4292.     add    bl, [@@PartNum]
  4293.     add    al, bl
  4294.     mov    [@@PartNum], al
  4295.     cmp    di, [@@LastByte]
  4296.     ja    @@SetPart
  4297.     mov    al, [es:di]
  4298.     cmp    al, ' '
  4299.     jne    @@errBadOption
  4300. @@SetPart:
  4301.     mov     [@@DrvSpecd], 1           ; Drive letter specified
  4302.     movzx    bx, [@@PartNum]
  4303.     mov    al, [@@DriveNo]
  4304.     cmp    [Partitions+bx], 0FEh
  4305.     jne    @@errPartUsed
  4306.     mov    [Partitions+bx], al
  4307.     jmp    @@Next
  4308. @@SetDrvLetter:
  4309.     movzx    bx, [@@DriveNo]
  4310.     mov    [UnInstallDrv+bx], 1    ; Uninstall this drive
  4311.     mov    [UnInstall], 1
  4312.     mov    [SpecUninstall], 1    ; Drive to uninstall specified
  4313.     jmp    @@Next
  4314.  
  4315. @@Parse1:
  4316. ; Check for switches
  4317.     mov     al, '/'
  4318.     scasb
  4319.     jne     @@errBadOption
  4320. ; Switch
  4321.     cmp     di, [@@LastByte]
  4322.     ja      @@errBadOption
  4323.     mov     dl, [es:di]
  4324.     inc     di
  4325.     mov     ax, 6520h        ; Upper case
  4326.     int     21h
  4327.     cmp    dl, 'B'
  4328.     je    @@BIOSExtension
  4329.     cmp    dl, 'C'
  4330.     je      @@CacheSize
  4331.     cmp     dl, 'U'
  4332.     je      @@UnInstall
  4333.     cmp    dl, 'L'
  4334.     je    @@ConvertLong
  4335.         cmp     dl, 'M'
  4336.         je      @@Multitrack
  4337.     jmp     @@errBadOption
  4338. ; /B switch
  4339. @@BIOSExtension:
  4340.     mov    [UseExtCHS], 1
  4341.     cmp     di, [@@LastByte]
  4342.     ja      @@Next
  4343.     cmp     [BYTE es:di], ' '
  4344.     jne     @@errBadOption
  4345.     jmp     @@Next
  4346. ; /C switch
  4347. @@CacheSize:
  4348.     cmp    [@@CacheOpt], 0
  4349.     jnz    @@errBadOption        ; Only one /C option allowed
  4350.     mov    [@@CacheOpt], 1
  4351.     mov     [Install], 1          ; /C switch - installing
  4352.     cmp     di, [@@LastByte]
  4353.     je      @@errBadOption
  4354.     mov     al, '='
  4355.     scasb
  4356.     jne     @@errBadOption
  4357.     xor     eax, eax              ; AX=Converted number
  4358. @@GetDigit:
  4359.     cmp     di, [@@LastByte]
  4360.     ja      @@ConvDone
  4361.     cmp     [BYTE es:di], ' '
  4362.     je      @@ConvDone
  4363.     mov     bx, 10
  4364.     mul     bx
  4365.     or      dx, dx
  4366.     jne     @@errBadOption
  4367.     cmp     [BYTE es:di], '0'
  4368.     jb      @@errBadOption
  4369.     cmp     [BYTE es:di], '9'
  4370.     ja      @@errBadOption
  4371.     add     al, [es:di]
  4372.     adc     ah, 0
  4373.     jc      @@errBadOption
  4374.     sub     ax, 30h
  4375.     inc     di
  4376.     jmp     @@GetDigit
  4377. @@ConvDone:
  4378.     cmp     ax, MinCacheSize        ; Check that cache size is ok.
  4379.     jb      @@errBadOption
  4380.     cmp     ax, MaxCacheSize
  4381.     ja      @@errBadOption
  4382.     mov     ecx, eax
  4383.     shl     eax, 10                 ; Bytes
  4384.     mov     edx, eax
  4385.     shr     edx, 16                 ; Cache size in bytes in DX:AX
  4386.     mov     bx, 512+CacheEntrySize+2
  4387.     div     bx                      ; Cache entries
  4388.     mov     [CacheEntries], ax
  4389.     mov     [FreeEntry], ax
  4390.     dec     [FreeEntry]
  4391.     xor     dx, dx
  4392.     mov     bx, LoadFactor
  4393.     div     bx                      ; Hash table size=entries/load factor
  4394.     mov     [HashSize], ax
  4395. ; Allocate the XMS memory blocks
  4396.     cmp     [XMSFound], 0
  4397.     je      @@errXMSFailure
  4398.     
  4399.     mov     dx, [CacheEntries]
  4400.     shr     dx, 1
  4401.     adc     dx, 0                   ; KB needed for the sectors
  4402.     mov     ah, 09h
  4403.     call    [XMSEntry]
  4404.     or      ax, ax
  4405.     jz      @@errAllocFailed
  4406.     mov     [hCacheSectors], dx
  4407.     inc     [XMSBlocks]
  4408.  
  4409.     mov     ax, [CacheEntries]
  4410.     mov     dx, CacheEntrySize
  4411.     mul     dx
  4412.     shl     edx, 16
  4413.     mov     dx, ax
  4414.     shr     edx, 10
  4415.     inc     dx
  4416.     mov     ah, 09h
  4417.     call    [XMSEntry]
  4418.     or      ax, ax
  4419.     jz      @@errAllocFailed
  4420.     mov     [hCacheLists], dx
  4421.     inc     [XMSBlocks]
  4422.  
  4423.     mov     dx, [HashSize]
  4424.     shr     dx, 9
  4425.     inc     dx                      ; KB needed for hash table
  4426.     mov     ah, 09h
  4427.     call    [XMSEntry]
  4428.     or      ax, ax
  4429.     jz      @@errAllocFailed
  4430.     mov     [hHashTable], dx
  4431.     inc     [XMSBlocks]
  4432.     mov     [CacheOn], 1
  4433. ; Clear the hash table        
  4434.     push    ds es
  4435.     push    di
  4436.     mov     ax, fs
  4437.     mov     ds, ax
  4438.     mov     es, ax
  4439.     ASSUME  ds:ResCode,es:ResCode
  4440.     mov     di, OFFSET Buf1
  4441.     mov     cx, 100h
  4442.     xor     eax, eax
  4443.     rep stosd                       ; Clear Buf1-Buf2
  4444.     
  4445.     mov     cx, [HashSize]
  4446.     shr     cx, 9
  4447.     inc     cx
  4448.     mov     [XMoveStruc.Length], 1024
  4449.     mov     [XMoveStruc.SourceHandle], 0
  4450.     mov     [WORD XMoveStruc.SourceOffset], OFFSET Buf1
  4451.     mov     [WORD (XMoveStruc.SourceOffset)+2], ds
  4452.     mov     ax, [hHashTable]
  4453.     mov     [XMoveStruc.DestHandle], ax
  4454.     mov     [XMoveStruc.DestOffset], 0
  4455. @@InitCacheTbl:
  4456.     mov     ah, 0Bh
  4457.     mov     si, OFFSET XMoveStruc
  4458.     call    [XMSEntry]
  4459.     add     [XMoveStruc.DestOffset], 1024
  4460.     or      ax, ax
  4461.     loopnz  @@InitCacheTbl
  4462.     jz      @@errXMSFailure
  4463. ; Clear the sentinel, entry 0
  4464.     mov     ax, [hCacheLists]
  4465.     mov     [XMoveStruc.DestHandle], ax
  4466.     mov     [XMoveStruc.DestOffset], 0
  4467.     mov     [WORD XMoveStruc.SourceOffset], OFFSET Buf1
  4468.     mov     [XMoveStruc.Length], CacheEntrySize
  4469.     mov     ah, 0Bh
  4470.     call    [XMSEntry]
  4471.     jz      @@errXMSFailure
  4472.     pop     di
  4473.     pop     es ds
  4474.     ASSUME  ds:DSeg,es:NOTHING
  4475.     jmp     @@Next  
  4476. ; /U switch
  4477. @@UnInstall:
  4478.     mov     [UnInstall], 1          ; /U switch - uninstall
  4479.     mov    [@@UnInstallOpt], 1
  4480.     cmp     di, [@@LastByte]
  4481.     ja      @@Next
  4482.     cmp     [BYTE es:di], ' '
  4483.     jne     @@errBadOption
  4484.     jmp     @@Next
  4485. ; /L switch
  4486. @@ConvertLong:
  4487.     mov    [ConvertLong], 1
  4488.     cmp     di, [@@LastByte]
  4489.     ja      @@Next
  4490.     cmp     [BYTE es:di], ' '
  4491.     jne     @@errBadOption
  4492.     jmp     @@Next
  4493. @@Multitrack:
  4494.         mov     [Multitrack], 0
  4495.         cmp     di, [@@LastByte]
  4496.         ja      @@Next
  4497.         cmp     [BYTE es:di], ' '
  4498.         jne     @@errBadOption
  4499.         jmp     @@Next
  4500.  
  4501. @@errBadOption:
  4502.     Abort   7
  4503. @@errPartUsed:
  4504.     Abort    17        ; Partition already specified once
  4505. @@errXMSFailure:
  4506.     Abort   10
  4507. @@errAllocFailed:
  4508.     cmp     bl, 0A0h
  4509.     jne     @@errAlloc1
  4510.     Abort   9               ; Out of XMS
  4511. @@errAlloc1:
  4512.     cmp     bl, 0A1h
  4513.     jne     @@errAlloc2
  4514.     Abort   13              ; Out of XMS handles
  4515. @@errAlloc2:
  4516.     Abort   10
  4517. @@Done:
  4518.     mov    al, [UnInstall]
  4519.     xor    al, [@@UnInstallOpt]
  4520.     jnz    @@errBadOption    ; Can't specify "/U" xor "d:"
  4521.     mov     al, [Install]
  4522.     test    al, [UnInstall]
  4523.     jnz     @@errBadOption    ; Can't both install and uninstall
  4524.     or    al, al
  4525.     jz    @@Exit
  4526.     cmp    [@@DrvSpecd], 0
  4527.     jz    @@Exit
  4528. ; Mark unwanted partition numbers
  4529.     mov    bx, -1
  4530. @@MarkPart:
  4531.     inc    bx
  4532.     cmp    bx, 26
  4533.         ja    @@Exit
  4534.     cmp    [Partitions+bx], 0FEh
  4535.     jne    @@MarkPart
  4536.     mov    [Partitions+bx], 0FFh
  4537.         jmp    @@MarkPart
  4538. @@Exit:
  4539.     ret
  4540. ENDP    ParseCmdLine
  4541.  
  4542. ENDS    CSeg
  4543.  
  4544. ;--------------------------------------------- Data for transient section
  4545. SEGMENT DSeg
  4546. MsgHello DB     "iHPFS     An installable HPFS driver for DOS       "
  4547.         DB      'Version 1.24    97-06-01',10,13
  4548.         DB      "Copyright (C) 1993-1997, Marcus Better.",10,13,10,13,"$"
  4549. MsgInstalled DB "Installed as "
  4550. MsgDrvLetter DB 0, ":",10,13,"$"
  4551. MsgUnInstalled DB "Driver uninstalled.",10,13,"$"
  4552. MsgDrvRemoved DB "Removed drive "
  4553. MsgDrvRemovedLetter DB "A:",10,13,"$"
  4554. DiskNumber DB    80h    ; Hard disk number
  4555. PartCount DB    0    ; Partition counter
  4556. Partitions DB    27 DUP(0FEh) ; Maps partitions to drive letters.
  4557.             ; 0FFh=don't install, 0FEh=first free drv letter.
  4558. XMSFound DB     0       ; Flag: XMS Found?
  4559. XMSBlocks DB    0       ; XMS blocks allocated
  4560. Install DB      0       ; Install iHPFS
  4561. UnInstall DB    0       ; Uninstall iHPFS. Error if both Install AND UnInstall
  4562. UnInstallDrv DB    26 DUP(0); Uninstall drive if set.
  4563. SpecUninstall DB 0    ; Set if specific drives are to be uninstalled.
  4564. DriverLoaded DB    0    ; Set if iHPFS loaded (during uninstall)
  4565. Installed DB    0    ; Set if any drive successfully installed,
  4566.             ; or if any uninstalled.
  4567. ErrSignaled DB  0       ; Error message displayed in ScanPartTbl
  4568. ExtPartBase DD    0    ; Base of extended partition offsets
  4569. UseExtCHS DB    0    ; Use extended CHS addressing
  4570. DiskAddrPkt1 DiskAddrPacketStruct <>
  4571.  
  4572. ; DOS info
  4573. LstOfLst DD     0       ; Address of List of Lists
  4574. LastDrive DB    0
  4575. DrDos    DB    0    ; Nonzero if DR-DOS (zero if Novell DOS)
  4576. CDSSize    DW    58h    ; Size of CDS entry
  4577.  
  4578. ; Error message table
  4579. ErrMsgTbl DW    MsgBadDOSVer            ; Error 0
  4580.     DW      MsgMallocErr            ; Error 1
  4581.     DW      MsgNoAvailDrvLetter     ; Error 2
  4582.     DW      MsgNoSDA                ; Error 3
  4583.     DW      MsgDiskError            ; Error 4
  4584.     DW      MsgNoPart               ; Error 5
  4585.     DW      MsgDrvUsed              ; Error 6
  4586.     DW      MsgBadCmdLine           ; Error 7
  4587.     DW      MsgInvDrv        ; Error 8
  4588.     DW      MsgOutOfXMS             ; Error 9
  4589.     DW      MsgXMSAllocFailed       ; Error 10
  4590.     DW      MsgCantUninstall        ; Error 11
  4591.     DW      MsgCantRmDrv            ; Error 12
  4592.     DW      MsgOutOfXMSHandles      ; Error 13
  4593.     DW    MsgNoMux        ; Error 14
  4594.     DW    MsgNotLoaded        ; Error 15
  4595.     DW    MsgDrvNotInst        ; Error 16
  4596.     DW    MsgOneDrivePerPart    ; Error 17
  4597.     DW    MsgPartNotFound        ; Error 18
  4598.     DW    MsgPartInstalled    ; Error 19
  4599.                                 
  4600. ; Error messages
  4601. MsgBadDOSVer     DB     "Wrong DOS version.",10,13,"$"
  4602. MsgMallocErr     DB     "Memory allocation error.",10,13,"$"
  4603. MsgNoAvailDrvLetter DB    "Out of drive letters.",10,13,"$"
  4604. MsgNoSDA     DB     "Compatibility error.",10,13,"$"
  4605. MsgDiskError     DB     "Error reading disk.",10,13,"$"
  4606. MsgNoPart     DB     "Cannot find HPFS partition.",10,13,"$"
  4607. MsgDrvUsed     DB     "Drive "
  4608. MsgDrvUsedLetter DB    "A: already in use.",10,13,"$"
  4609. MsgInvDrv    DB     "Invalid drive letter "
  4610. MsgInvDrvLetter    DB    "A:",10,13,"$"
  4611. MsgBadCmdLine    DB     "Invalid command line arguments.",10,13
  4612.         DB      "Syntax: IHPFS [options] [d:n d:n ...]",10,13
  4613.         DB    "        IHPFS /U [d:]",10,13
  4614.         DB      "where d is a drive letter and n is the number of the "
  4615.         DB      "HPFS partition.",10,13,10,13
  4616.         DB      "Options:",10,13
  4617.         DB    "/B           Use more BIOS extensions to access high partitions.",10,13
  4618.         DB      "/C=x         Allocate x KB for cache. Must be between 32 and 32768.",10,13
  4619.         DB    "/L           Convert long filenames.",10,13
  4620.                 DB      "/M           Disable multitrack operations.",10,13
  4621.         DB      "/U           Uninstall driver.",10,13
  4622.         DB    "$"
  4623. MsgOutOfXMS     DB    "Out of XMS memory.",10,13,"$"
  4624. MsgXMSAllocFailed DB    "Cannot allocate XMS memory.",10,13,"$"
  4625. MsgCantUninstall DB    "Cannot unload driver.",10,13,"$"
  4626. MsgCantRmDrv    DB    "Couldn't uninstall drive "
  4627. MsgCantRmDrvLetter DB    "A:",10,13,"$"
  4628. MsgOutOfXMSHandles DB    "Out of XMS handles.",10,13,"$"
  4629. MsgNoMux     DB    "No free multiplex function found.",10,13,"$"
  4630. MsgNotLoaded    DB    "iHPFS not loaded.",10,13,"$"
  4631. MsgDrvNotInst    DB    "iHPFS is not installed for drive "
  4632. MsgDrvNotInstLetter DB "A:",10,13,"$"
  4633. MsgOneDrivePerPart DB    "Cannot install two iHPFS drives for the same partition.",10,13,"$"
  4634. MsgPartNotFound    DB    "Partition "
  4635. MsgPartNotFoundNr DB    " 0 not found.",10,13,"$"
  4636. MsgPartInstalled DB    "iHPFS already installed for partition "
  4637. MsgPartInstalledNr DB    " 0.",10,13,"$"
  4638. MsgBadPartTable    DB    "Bad partition table signature.",10,13,"$"
  4639. ENDS    DSeg
  4640.  
  4641. SEGMENT SSeg    STACK
  4642.     DB      128 DUP ('STACK---')
  4643. ENDS    SSeg
  4644.  
  4645.     END     Main
  4646.